diff --git a/drivers/serial/Tupfile.lua b/drivers/serial/Tupfile.lua new file mode 100644 index 0000000000..6935f13862 --- /dev/null +++ b/drivers/serial/Tupfile.lua @@ -0,0 +1,3 @@ +if tup.getconfig("NO_FASM") ~= "" then return end +ROOT = "../.." +tup.rule("serial.asm", "fasm %f %o " .. tup.getconfig("PESTRIP_CMD") .. tup.getconfig("KPACK_CMD"), "%B.sys") diff --git a/drivers/serial/common.inc b/drivers/serial/common.inc new file mode 100755 index 0000000000..7a4cd17155 --- /dev/null +++ b/drivers/serial/common.inc @@ -0,0 +1,272 @@ +SERIAL_API_GET_VERSION = 0 +SERIAL_API_SRV_ADD_PORT = 1 +SERIAL_API_SRV_REMOVE_PORT = 2 +SERIAL_API_SRV_HANDLE_EVENT = 3 +SERIAL_API_OPEN_PORT = 4 +SERIAL_API_CLOSE_PORT = 5 +SERIAL_API_SETUP_PORT = 6 +SERIAL_API_READ = 7 +SERIAL_API_WRITE = 8 + +SERIAL_API_ERR_PORT_INVALID = 1 +SERIAL_API_ERR_PORT_BUSY = 2 +SERIAL_API_ERR_CONF = 3 + +SERIAL_EVT_TXE = 1 ; tx fifo or register is empty, all data has been sent +SERIAL_EVT_RXNE = 2 ; rx fifo or register is not empty + +SERIAL_CONF_PARITY_NONE = 0 +SERIAL_CONF_PARITY_EVEN = 1 +SERIAL_CONF_PARITY_ODD = 2 +SERIAL_CONF_PARITY_MARK = 3 +SERIAL_CONF_PARITY_SPACE = 4 + +SERIAL_CONF_FLOW_CTRL_NONE = 0 + +struct SP_DRIVER + size dd ? ; size of this struct + startup dd ? ; void __stdcall (*startup)(void *drv_data, const struct serial_conf *conf); + shutdown dd ? ; void __stdcall (*shutdown)(void *drv_data); + reconf dd ? ; void __stdcall (*reconf)(void *drv_data, const struct serial_conf *conf); + tx dd ? ; void __stdcall (*tx)(void *drv_data); +ends + +struct SP_CONF + size dd ? ; size of this struct + baudrate dd ? + word_size db ? + stop_bits db ? + parity db ? + flow_ctrl db ? +ends + +proc serial_add_port stdcall, drv:dword, drv_data:dword +locals + handler dd ? + io_code dd ? + input dd ? + inp_size dd ? + output dd ? + out_size dd ? +endl + mov eax, [serial_drv_entry] + mov [handler], eax + mov [io_code], SERIAL_API_SRV_ADD_PORT + lea eax, [drv] + mov [input], eax + mov [inp_size], 8 + xor eax, eax + mov [output], eax + mov [out_size], eax + lea eax, [handler] + push esi edi + invoke ServiceHandler, eax + pop edi esi + ret +endp + +proc serial_remove_port stdcall, port:dword +locals + handler dd ? + io_code dd ? + input dd ? + inp_size dd ? + output dd ? + out_size dd ? +endl + mov eax, [serial_drv_entry] + mov [handler], eax + mov [io_code], SERIAL_API_SRV_REMOVE_PORT + lea eax, [port] + mov [input], eax + mov [inp_size], 4 + xor eax, eax + mov [output], eax + mov [out_size], eax + lea eax, [handler] + push esi edi + invoke ServiceHandler, eax + pop edi esi + ret +endp + +; see SERIAL_EVT_* +proc serial_handle_event stdcall, port:dword, event:dword, count:dword, buff:dword +locals + handler dd ? + io_code dd ? + input dd ? + inp_size dd ? + output dd ? + out_size dd ? +endl + mov eax, [serial_drv_entry] + mov [handler], eax + mov [io_code], SERIAL_API_SRV_HANDLE_EVENT + lea eax, [port] + mov [input], eax + mov [inp_size], 16 + xor eax, eax + mov [output], eax + mov [out_size], eax + lea eax, [handler] + push esi edi + invoke ServiceHandler, eax + pop edi esi + ret +endp + +proc serial_port_init + lea ecx, [serial_drv_name] + mcall SF_SYS_MISC, SSF_LOAD_DRIVER + mov [serial_drv_handle], eax + ret +endp + +proc serial_port_open stdcall uses ebx, port_id:dword, conf:dword, handle:dword +locals + .handler dd ? + .io_code dd ? + .input dd ? + .inp_size dd ? + .output dd ? + .out_size dd ? +endl + push [conf] + push [port_id] + mov eax, [serial_drv_handle] + mov [.handler], eax + mov dword [.io_code], SERIAL_API_OPEN_PORT + mov [.input], esp + mov dword [.inp_size], 8 + mov eax, [handle] + mov [.output], eax + mov dword [.out_size], 4 + + lea ecx, [.handler] + mcall SF_SYS_MISC, SSF_CONTROL_DRIVER + + add esp, 8 + ret +endp + +proc serial_port_close stdcall uses ebx, handle:dword +locals + .handler dd ? + .io_code dd ? + .input dd ? + .inp_size dd ? + .output dd ? + .out_size dd ? +endl + push [handle] + mov eax, [serial_drv_handle] + mov [.handler], eax + mov dword [.io_code], SERIAL_API_CLOSE_PORT + mov [.input], esp + mov dword [.inp_size], 4 + mov dword [.output], 0 + mov dword [.out_size], 0 + + lea ecx, [.handler] + mcall SF_SYS_MISC, SSF_CONTROL_DRIVER + + add esp, 4 + ret +endp + +proc serial_port_setup stdcall uses ebx, handle:dword, conf:dword +locals + .handler dd ? + .io_code dd ? + .input dd ? + .inp_size dd ? + .output dd ? + .out_size dd ? +endl + push [conf] + push [handle] + mov eax, [serial_drv_handle] + mov [.handler], eax + mov dword [.io_code], SERIAL_API_SETUP_PORT + mov [.input], esp + mov dword [.inp_size], 8 + sub esp, 4 + mov [.output], esp + mov dword [.out_size], 4 + + lea ecx, [.handler] + mcall SF_SYS_MISC, SSF_CONTROL_DRIVER + + pop eax + add esp, 8 + ret +endp + +proc serial_port_read stdcall uses ebx, handle:dword, dest:dword, count_ptr:dword +locals + .handler dd ? + .io_code dd ? + .input dd ? + .inp_size dd ? + .output dd ? + .out_size dd ? +endl + mov eax, [count_ptr] + push dword [eax] + push [dest] + push [handle] + mov eax, [serial_drv_handle] + mov [.handler], eax + mov dword [.io_code], SERIAL_API_READ + mov [.input], esp + mov dword [.inp_size], 12 + sub esp, 4 + mov [.output], esp + mov [.out_size], 4 + + lea ecx, [.handler] + mcall SF_SYS_MISC, SSF_CONTROL_DRIVER + + pop ecx + mov edx, [count_ptr] + mov [edx], ecx + add esp, 12 + ret +endp + +proc serial_port_write stdcall uses ebx, handle:dword, src:dword, count_ptr:dword +locals + .handler dd ? + .io_code dd ? + .input dd ? + .inp_size dd ? + .output dd ? + .out_size dd ? +endl + mov eax, [count_ptr] + push dword [eax] + push [src] + push [handle] + mov eax, [serial_drv_handle] + mov [.handler], eax + mov dword [.io_code], SERIAL_API_WRITE + mov [.input], esp + mov dword [.inp_size], 12 + sub esp, 4 + mov dword [.output], esp + mov dword [.out_size], 4 + + lea ecx, [.handler] + mcall SF_SYS_MISC, SSF_CONTROL_DRIVER + + pop ecx + mov edx, [count_ptr] + mov [edx], ecx + add esp, 12 + ret +endp + +align 4 +serial_drv_name db "SERIAL", 0 +serial_drv_handle dd ? diff --git a/drivers/serial/ring_buf.inc b/drivers/serial/ring_buf.inc new file mode 100755 index 0000000000..2bd17ea098 --- /dev/null +++ b/drivers/serial/ring_buf.inc @@ -0,0 +1,178 @@ +struct RING_BUF + start_ptr dd ? ; Pointer to start of buffer + end_ptr dd ? ; Pointer to end of buffer + read_ptr dd ? ; Read pointer + write_ptr dd ? ; Write pointer + size dd ? ; Size of buffer +ends + +;bool __fastcall ring_buf_create(struct RING_BUF *buf, u32 size) +align 4 +proc ring_buf_create + push ecx + push edx + invoke CreateRingBuffer, edx, PG_SW + pop edx + pop ecx + test eax, eax + jz .exit + + mov [ecx + RING_BUF.start_ptr], eax + mov [ecx + RING_BUF.write_ptr], eax + mov [ecx + RING_BUF.read_ptr], eax + add eax, edx + mov [ecx + RING_BUF.end_ptr], eax + mov [ecx + RING_BUF.size], edx + or eax, 1 +.exit: + ret +endp + +;void __fastcall ring_buf_destroy(struct RING_BUF *buf) +align 4 +proc ring_buf_destroy + xor eax, eax + mov [ecx + RING_BUF.write_ptr], eax + mov [ecx + RING_BUF.read_ptr], eax + mov [ecx + RING_BUF.end_ptr], eax + mov [ecx + RING_BUF.size], eax + xchg eax, [ecx + RING_BUF.start_ptr] + invoke KernelFree, eax + ret +endp + +;u32 __fastcall ring_buf_count(struct RING_BUF *buf) +align 4 +proc ring_buf_count + mov eax, [ecx + RING_BUF.write_ptr] + mov edx, [ecx + RING_BUF.read_ptr] + cmp eax, edx + jb @f + sub eax, edx + ret + + @@: + sub eax, edx + add eax, [ecx + RING_BUF.size] + ret +endp + +;u32 __fastcall ring_buf_free(struct RING_BUF *buf) +align 4 +proc ring_buf_free + mov eax, [ecx + RING_BUF.read_ptr] + mov edx, [ecx + RING_BUF.write_ptr] + cmp eax, edx + jae @f + sub eax, edx + dec eax + ret + + @@: + sub eax, edx + dec eax + add eax, [ecx + RING_BUF.size] + ret +endp + +;u32 __stdcall ring_buf_write(struct RING_BUF *buf, const u32 *src, u32 size) +align 4 +proc ring_buf_write stdcall uses esi edi, buf, src, size + mov ecx, [buf] + call ring_buf_free + test eax, eax + jz .exit + + mov esi, [src] + mov edi, [ecx + RING_BUF.write_ptr] + mov ecx, [size] + cmp ecx, eax + jbe .copy + mov ecx, eax + .copy: + mov eax, ecx + cld + shr ecx, 1 + jnc .nb + movsb + .nb: + shr ecx, 1 + jnc .nw + movsw + .nw: + test ecx, ecx + jz .nd + rep movsd + .nd: + mov ecx, [buf] + cmp edi, [ecx + RING_BUF.end_ptr] + jb @f + sub edi, [ecx + RING_BUF.size] + @@: + mov [ecx + RING_BUF.write_ptr], edi + .exit: + ret +endp + +;u32 __stdcall ring_buf_read(struct RING_BUF *buf, u32 *dst, u32 size) +align 4 +proc ring_buf_read stdcall uses ebx esi edi, buf, dst, size + mov ecx, [buf] + call ring_buf_count + test eax, eax + jz .exit + + mov esi, [ecx + RING_BUF.read_ptr] + mov edi, [dst] + mov ecx, [size] + cmp ecx, eax + jbe .copy + mov ecx, eax + .copy: + mov eax, ecx + shr ecx, 1 + jnc .nb + cld + movsb + .nb: + shr ecx, 1 + jnc .nw + movsw + .nw: + test ecx, ecx + jz .nd + rep movsd + .nd: + mov ecx, [buf] + cmp esi, [ecx + RING_BUF.end_ptr] + jb .save_ptr + sub esi, [ecx + RING_BUF.size] + .save_ptr: + mov [ecx + RING_BUF.read_ptr], esi + .exit: + ret +endp + +;u32 __fastcall ring_buf_discard(struct RING_BUF *buf, u32 size) +align 4 +proc ring_buf_discard + push ecx + push edx + call ring_buf_count + pop edx + pop ecx + + cmp eax, edx + jae .discard + mov edx, eax + .discard: + push edx + add edx, [ecx + RING_BUF.read_ptr] + cmp edx, [ecx + RING_BUF.end_ptr] + jb .save_ptr + sub edx, [ecx + RING_BUF.size] + .save_ptr: + mov [ecx + RING_BUF.read_ptr], edx + pop eax + ret +endp diff --git a/drivers/serial/serial.asm b/drivers/serial/serial.asm new file mode 100755 index 0000000000..0155207b49 --- /dev/null +++ b/drivers/serial/serial.asm @@ -0,0 +1,675 @@ +format PE DLL native 0.05 +entry START + +L_DBG = 1 +L_ERR = 2 + +__DEBUG__ = 0 +__DEBUG_LEVEL__ = L_DBG + +SERIAL_RING_BUF_SIZE = 32768 + +API_VERSION = 1 + +section '.flat' readable writable executable + +include '../struct.inc' +include '../proc32.inc' +include '../fdo.inc' +include '../macros.inc' +include '../peimport.inc' + +include 'common.inc' +include 'ring_buf.inc' +include 'uart16550.inc' + +struct SERIAL_OBJ + magic dd ? + destroy dd ? + fd dd ? + bk dd ? + pid dd ? + port dd ? ; pointer to SERIAL_PORT +ends + +struct SERIAL_PORT + fd dd ? + bk dd ? + id dd ? ; unique port number + mtx MUTEX + con dd ? ; pointer to SERIAL_OBJ + drv dd ? ; pointer to struct SP_DRIVER + drv_data dd ? ; pointer to driver-defined data + rx_buf RING_BUF + tx_buf RING_BUF + conf SP_CONF +ends + +proc START c, reason:dword + cmp [reason], DRV_ENTRY + jne .fail + + mov ecx, port_list_mutex + invoke MutexInit + + stdcall uart_probe, 0x3f8, 4 + stdcall uart_probe, 0x2f8, 3 + stdcall uart_probe, 0x3e8, 4 + stdcall uart_probe, 0x2e8, 3 + invoke RegService, drv_name, service_proc + ret + + .fail: + xor eax, eax + ret +endp + +srv_calls: + dd service_proc.get_version + dd service_proc.drv_add_port + dd service_proc.drv_remove_port + dd service_proc.drv_handle_event + dd service_proc.open + dd service_proc.close + dd service_proc.setup + dd service_proc.read + dd service_proc.write + ; TODO enumeration +srv_calls_end: + +proc service_proc stdcall uses ebx esi edi, ioctl:dword + mov edx, [ioctl] + mov eax, [edx + IOCTL.io_code] + cmp eax, (srv_calls_end - srv_calls) / 4 + jae .err + jmp dword [srv_calls + eax * 4] + + .get_version: + cmp [edx + IOCTL.out_size], 4 + jb .err + mov edx, [edx + IOCTL.output] + mov dword [edx], API_VERSION + xor eax, eax + ret + + .drv_add_port: + ; in: + ; +0: driver + ; +4: driver data + cmp [edx + IOCTL.inp_size], 8 + jb .err + mov ebx, [edx + IOCTL.input] + mov ecx, [ebx] + mov edx, [ebx + 4] + call add_port + ret + + .drv_remove_port: + ; in: + ; +0: port handle + cmp [edx + IOCTL.inp_size], 4 + jb .err + mov ebx, [edx + IOCTL.input] + mov ecx, [ebx] + call remove_port + ret + + .drv_handle_event: + ; in: + ; +0: port handle + ; +4: event + ; +8: count + ; +12: buf + cmp [edx + IOCTL.inp_size], 16 + jb .err + mov ebx, [edx + IOCTL.input] + mov eax, [ebx] + mov edx, [ebx + 4] + mov ecx, [ebx + 8] + mov esi, [ebx + 12] + call handle_event + ret + + .open: + ; in: + ; +0 port number + ; +4 addr to SERIAL_CONF + ; out: + ; +0 port handle if success + cmp [edx + IOCTL.inp_size], 8 + jb .err + cmp [edx + IOCTL.out_size], 4 + jb .err + mov ebx, [edx + IOCTL.input] + mov ecx, [edx + IOCTL.output] + stdcall sp_open, [ebx], [ebx + 4], ecx + ret + + .close: + ; in: + ; +0 port handle + cmp [edx + IOCTL.inp_size], 4 + jb .err + mov ecx, [edx + IOCTL.input] + mov ecx, [ecx] + call sp_close + ret + + .setup: + ; in: + ; +0 port handle + ; +4 addr to SERIAL_CONF + ; out: + ; +0 result + cmp [edx + IOCTL.inp_size], 8 + jb .err + cmp [edx + IOCTL.out_size], 4 + jb .err + mov ebx, [edx + IOCTL.input] + push edx + mov eax, [ebx] + mov esi, [ebx + 4] + call sp_setup + pop edx + mov ebx, [edx + IOCTL.output] + mov [ebx], eax + ret + + .read: + ; in: + ; +0 port handle + ; +4 addr of dest buf + ; +8 count to read + ; out: + ; +0 bytes read + cmp [edx + IOCTL.inp_size], 12 + jb .err + cmp [edx + IOCTL.out_size], 4 + jb .err + mov ebx, [edx + IOCTL.input] + push edx + mov eax, [ebx] + mov edi, [ebx + 4] + mov ecx, [ebx + 8] + call sp_read + pop edx + mov ebx, [edx + IOCTL.output] + mov [ebx], ecx + ret + + .write: + ; in: + ; +0 port handle + ; +4 addr to source buf + ; +8 count to write + ; out: + ; +0 bytes written + cmp [edx + IOCTL.inp_size], 12 + jb .err + cmp [edx + IOCTL.out_size], 4 + jb .err + mov ebx, [edx + IOCTL.input] + push edx + mov eax, [ebx] + mov esi, [ebx + 4] + mov ecx, [ebx + 8] + call sp_write + pop edx + mov ebx, [edx + IOCTL.output] + mov [ebx], ecx + ret + + .err: + or eax, -1 + ret +endp + +; struct SERIAL_PORT __fastcall *add_port(const struct SP_DRIVER *drv, const void *drv_data); +align 4 +proc add_port uses edi + DEBUGF L_DBG, "serial.sys: add port drv=%x drv_data=%x\n", ecx, edx + + mov eax, [ecx + SP_DRIVER.size] + cmp eax, sizeof.SP_DRIVER + jne .fail + + ; alloc memory for serial port descriptor + push ecx + push edx + movi eax, sizeof.SERIAL_PORT + invoke Kmalloc + pop edx + pop ecx + test eax, eax + jz .fail + + ; initialize fields of descriptor + mov edi, eax + mov [edi + SERIAL_PORT.drv], ecx + mov [edi + SERIAL_PORT.drv_data], edx + lea ecx, [edi + SERIAL_PORT.mtx] + invoke MutexInit + and [edi + SERIAL_PORT.con], 0 + + mov ecx, port_list_mutex + invoke MutexLock + + ; TODO obtain unused id's + mov eax, [port_count] + mov [edi + SERIAL_PORT.id], eax + inc [port_count] + + ; add port to linked list + mov eax, port_list + mov ecx, [eax + SERIAL_PORT.bk] + mov [edi + SERIAL_PORT.bk], ecx + mov [edi + SERIAL_PORT.fd], eax + mov [ecx + SERIAL_PORT.fd], edi + mov [eax + SERIAL_PORT.bk], edi + + DEBUGF L_DBG, "serial.sys: add port %x with id %x\n", edi, [edi + SERIAL_PORT.id] + + mov ecx, port_list_mutex + invoke MutexUnlock + + mov eax, edi + ret + + .fail: + xor eax, eax + ret +endp + +align 4 +; u32 __fastcall *remove_port(struct SERIAL_PORT *port); +proc remove_port uses esi + mov esi, ecx + mov ecx, port_list_mutex + invoke MutexLock + + lea ecx, [esi + SERIAL_PORT.mtx] + invoke MutexLock + + mov eax, [esi + SERIAL_PORT.con] + test eax, eax + jz @f + push esi + call sp_destroy + pop esi + @@: + + mov eax, [esi + SERIAL_PORT.fd] + mov edx, [esi + SERIAL_PORT.bk] + mov [edx + SERIAL_PORT.fd], eax + mov [eax + SERIAL_PORT.bk], edx + DEBUGF L_DBG, "serial.sys: remove port %x with id %x\n", esi, [esi + SERIAL_PORT.id] + mov eax, esi + invoke Kfree + + mov ecx, port_list_mutex + invoke MutexUnlock + + xor eax, eax + ret +endp + +align 4 +; @param eax port +; @param edx event +; @param ecx count +; @param esi buffer +; @return eax count +proc handle_event uses edi + mov edi, eax + cmp edx, SERIAL_EVT_RXNE + jz .rx + cmp edx, SERIAL_EVT_TXE + jz .tx + xor eax, eax + jmp .exit + .rx: + lea eax, [edi + SERIAL_PORT.rx_buf] + stdcall ring_buf_write, eax, esi, ecx + jmp .exit + .tx: + lea eax, [edi + SERIAL_PORT.tx_buf] + stdcall ring_buf_read, eax, esi, ecx + ; fallthrough + .exit: + ret +endp + +align 4 +proc sp_validate_conf + mov eax, [ecx + SP_CONF.size] + cmp eax, sizeof.SP_CONF + jnz .fail + mov eax, [ecx + SP_CONF.baudrate] + test eax, eax + jz .fail + mov al, [ecx + SP_CONF.word_size] + cmp al, 8 + jne .fail ; TODO implement different word size + mov al, [ecx + SP_CONF.stop_bits] + cmp al, 1 + jne .fail ; TODO implement different stop bits count + mov al, [ecx + SP_CONF.parity] + cmp al, SERIAL_CONF_PARITY_NONE + jne .fail ; TODO implement parity + mov al, [ecx + SP_CONF.flow_ctrl] + cmp al, SERIAL_CONF_FLOW_CTRL_NONE + jne .fail ; TODO implement flow control + .ok: + xor eax, eax + ret + .fail: + or eax, -1 + ret +endp + +align 4 +proc sp_open stdcall uses ebx esi edi, port_id:dword, conf:dword, handle:dword + DEBUGF L_DBG, "serial.sys: sp_open %x %x %x\n", [port_id], [conf], [handle] + + mov ecx, [conf] + call sp_validate_conf + test eax, eax + jz @f + mov eax, SERIAL_API_ERR_CONF + ret + @@: + mov edi, [conf] + + ; get access to the serial ports list + mov ecx, port_list_mutex + invoke MutexLock + + ; find port by id + mov eax, [port_id] + mov esi, port_list + .find_port: + mov esi, [esi + SERIAL_PORT.fd] + cmp esi, port_list + jz .not_found + mov ecx, [esi + SERIAL_PORT.id] + cmp ecx, eax + jz .found + jmp .find_port + + .not_found: + DEBUGF L_DBG, "serial.sys: port not found\n" + mov eax, SERIAL_API_ERR_PORT_INVALID + jmp .unlock_list + + .found: + DEBUGF L_DBG, "serial.sys: found port %x\n", esi + + ; get access to serial port + lea ecx, [esi + SERIAL_PORT.mtx] + invoke MutexLock + + ; availability check + cmp [esi + SERIAL_PORT.con], 0 + jz .open + mov eax, SERIAL_API_ERR_PORT_BUSY + jmp .unlock_port + + .open: + ; create rx and tx ring buffers + lea ecx, [esi + SERIAL_PORT.rx_buf] + mov edx, SERIAL_RING_BUF_SIZE + call ring_buf_create + test eax, eax + jnz @f + jmp .unlock_port + @@: + lea ecx, [esi + SERIAL_PORT.tx_buf] + mov edx, SERIAL_RING_BUF_SIZE + call ring_buf_create + test eax, eax + jnz @f + jmp .free_rx_buf + @@: + invoke GetPid + mov ebx, eax + mov eax, sizeof.SERIAL_OBJ + invoke CreateObject + test eax, eax + jnz @f + or eax, -1 + jmp .free_tx_buf + @@: + DEBUGF L_DBG, "serial.sys: created object %x\n", eax + + ; save port handle + mov ecx, [handle] + mov [ecx], eax + mov [eax + SERIAL_OBJ.magic], 'UART' + mov [eax + SERIAL_OBJ.destroy], sp_destroy + mov [eax + SERIAL_OBJ.port], esi + + ; fill fields + mov [esi + SERIAL_PORT.con], eax + ; copy conf + mov eax, [edi + SP_CONF.size] + mov [esi + SERIAL_PORT.conf + SP_CONF.size], eax + mov eax, [edi + SP_CONF.baudrate] + mov [esi + SERIAL_PORT.conf + SP_CONF.baudrate], eax + mov eax, dword [edi + SP_CONF.word_size] + mov dword [esi + SERIAL_PORT.conf + SP_CONF.word_size], eax + + ; tell driver about port open + mov ebx, [esi + SERIAL_PORT.drv] + mov ecx, [esi + SERIAL_PORT.drv_data] + stdcall dword [ebx + SP_DRIVER.startup], ecx, edi + test eax, eax + jz .unlock_port + ; on error fallthrough + push eax + mov eax, [esi + SERIAL_PORT.con] + invoke DestroyObject + and [esi + SERIAL_PORT.con], 0 + pop eax + + .free_tx_buf: + push eax + lea ecx, [esi + SERIAL_PORT.tx_buf] + call ring_buf_destroy + pop eax + + .free_rx_buf: + push eax + lea ecx, [esi + SERIAL_PORT.rx_buf] + call ring_buf_destroy + pop eax + + .unlock_port: + push eax + lea ecx, [esi + SERIAL_PORT.mtx] + invoke MutexUnlock + pop eax + + .unlock_list: + push eax + mov ecx, port_list_mutex + invoke MutexUnlock + pop eax + + ret +endp + +align 4 +; @param ecx serial port handle +proc sp_close uses ebx esi + mov eax, ecx + cmp [eax + SERIAL_OBJ.magic], 'UART' + je .ok + or eax, -1 + ret + .ok: + mov ebx, [eax + SERIAL_OBJ.port] + push eax + lea ecx, [ebx + SERIAL_PORT.mtx] + invoke MutexLock + pop eax + + push ebx + call sp_destroy + pop ebx + + lea ecx, [ebx + SERIAL_PORT.mtx] + invoke MutexUnlock + + xor eax, eax + ret +endp + +align 4 +; @param eax port handle +; @param esi pointer to SP_CONF +; @return eax = 0 on success +proc sp_setup + test esi, esi + jz .fail + cmp [eax + SERIAL_OBJ.magic], 'UART' + jne .fail + mov ebx, eax + mov ecx, esi + call sp_validate_conf + test eax, eax + jz @f + DEBUGF L_DBG, "serial.sys: invalid conf %x\n", ecx + mov eax, SERIAL_API_ERR_CONF + ret + @@: + ; lock mutex + mov edi, [ebx + SERIAL_OBJ.port] + lea ecx, [edi + SERIAL_PORT.mtx] + invoke MutexLock + ; reconfigure port + mov eax, [edi + SERIAL_PORT.drv] + mov ecx, [edi + SERIAL_PORT.drv_data] + stdcall dword [eax + SP_DRIVER.reconf], ecx, esi + xor eax, eax + push eax + test eax, eax + jnz @f + ; copy conf if success + mov eax, [esi + SP_CONF.size] + mov [edi + SERIAL_PORT.conf + SP_CONF.size], eax + mov eax, [esi + SP_CONF.baudrate] + mov [edi + SERIAL_PORT.conf + SP_CONF.baudrate], eax + mov eax, dword [esi + SP_CONF.word_size] + mov dword [edi + SERIAL_PORT.conf + SP_CONF.word_size], eax + @@: + ; unlock mutex + lea ecx, [edi + SERIAL_PORT.mtx] + invoke MutexUnlock + pop eax + ret + .fail: + or eax, -1 + ret +endp + +align 4 +; @param eax serial obj +proc sp_destroy + mov esi, [eax + SERIAL_OBJ.port] + DEBUGF L_DBG, "serial.sys: destroy port %x\n", esi + + invoke DestroyObject + and [esi + SERIAL_PORT.con], 0 + + ; tell driver about port close + mov ebx, [esi + SERIAL_PORT.drv] + mov edx, [esi + SERIAL_PORT.drv_data] + stdcall dword [ebx + SP_DRIVER.shutdown], edx + + lea ecx, [esi + SERIAL_PORT.tx_buf] + call ring_buf_destroy + lea ecx, [esi + SERIAL_PORT.rx_buf] + call ring_buf_destroy + ret +endp + + +align 4 +; @param eax port handle +; @param ecx bytes count +; @param edi address of destination buffer +; @return eax = 0 on success and ecx = count bytes read +proc sp_read + test edi, edi + jz .fail + test ecx, ecx + jz .fail + cmp [eax + SERIAL_OBJ.magic], 'UART' + jne .fail + mov esi, [eax + SERIAL_OBJ.port] + push ecx ; last arg for ring_buf_read + lea ecx, [esi + SERIAL_PORT.mtx] + invoke MutexLock + lea eax, [esi + SERIAL_PORT.rx_buf] + stdcall ring_buf_read, eax, edi + push eax + lea ecx, [esi + SERIAL_PORT.mtx] + invoke MutexUnlock + pop ecx + xor eax, eax + ret + .fail: + or eax, -1 + ret +endp + +align 4 +; @param eax port handle +; @param ecx bytes count +; @param esi address of source buffer +; @return eax = 0 on success and ecx = count bytes written +proc sp_write + test esi, esi + jz .fail + test ecx, ecx + jz .fail + cmp [eax + SERIAL_OBJ.magic], 'UART' + jne .fail + + push ecx ; last arg for ring_buf_write + mov edi, [eax + SERIAL_OBJ.port] + lea ecx, [edi + SERIAL_PORT.mtx] + invoke MutexLock + + lea ecx, [edi + SERIAL_PORT.tx_buf] + stdcall ring_buf_write, ecx, esi + push eax + test eax, eax + jz @f + mov ebx, [edi + SERIAL_PORT.drv] + mov edx, [edi + SERIAL_PORT.drv_data] + stdcall dword [ebx + SP_DRIVER.tx], edx + @@: + lea ecx, [edi + SERIAL_PORT.mtx] + invoke MutexUnlock + pop ecx + xor eax, eax + ret + .fail: + or eax, -1 + ret +endp + +drv_name db 'SERIAL', 0 +include_debug_strings + +align 4 +port_count dd 0 +port_list_mutex MUTEX +port_list: + .fd dd port_list + .bk dd port_list + +align 4 +data fixups +end data diff --git a/drivers/serial/uart16550.inc b/drivers/serial/uart16550.inc new file mode 100755 index 0000000000..5c8a431e06 --- /dev/null +++ b/drivers/serial/uart16550.inc @@ -0,0 +1,408 @@ +BASE_FREQ = 1843200 +BASE_DIV = 16 + +THR_REG = 0 ; transtitter/reciever +IER_REG = 1 ; interrupt enable +IIR_REG = 2 ; interrupt info +FCR_REG = 2 ; FIFO control +LCR_REG = 3 ; line control +MCR_REG = 4 ; modem control +LSR_REG = 5 ; line status +MSR_REG = 6 ; modem status +SCR_REG = 7 ; scratch + +DLL_REG = THR_REG ; divisor latch (LSB) +DLM_REG = IER_REG ; divisor latch (MSB) + +LCR_5BIT = 0x00 +LCR_6BIT = 0x01 +LCR_7BIT = 0x02 +LCR_8BIT = 0x03 +LCR_STOP_1 = 0x00 +LCR_STOP_2 = 0x04 +LCR_PARITY = 0x08 +LCR_EVEN = 0x10 +LCR_STICK = 0x20 +LCR_BREAK = 0x40 +LCR_DLAB = 0x80 + +LSR_DR = 0x01 ; data ready +LSR_OE = 0x02 ; overrun error +LSR_PE = 0x04 ; parity error +LSR_FE = 0x08 ; framing error +LSR_BI = 0x10 ; break interrupt +LSR_THRE = 0x20 ; transmitter holding empty +LSR_TEMT = 0x40 ; transmitter empty +LSR_FER = 0x80 ; FIFO error + +FCR_EFIFO = 0x01 ; enable FIFO +FCR_CRB = 0x02 ; clear reciever FIFO +FCR_CXMIT = 0x04 ; clear transmitter FIFO +FCR_RDY = 0x08 ; set RXRDY and TXRDY pins +FCR_FIFO_1 = 0x00 ; 1 byte trigger +FCR_FIFO_4 = 0x40 ; 4 bytes trigger +FCR_FIFO_8 = 0x80 ; 8 bytes trigger +FCR_FIFO_14 = 0xC0 ; 14 bytes trigger + +IIR_INTR = 0x01 ; 1= no interrupts +IIR_IID = 0x0E ; interrupt source mask + +IER_RDAI = 0x01 ; reciever data interrupt +IER_THRI = 0x02 ; transmitter empty interrupt +IER_LSI = 0x04 ; line status interrupt +IER_MSI = 0x08 ; modem status interrupt + +MCR_DTR = 0x01 ; 0-> DTR=1, 1-> DTR=0 +MCR_RTS = 0x02 ; 0-> RTS=1, 1-> RTS=0 +MCR_OUT1 = 0x04 ; 0-> OUT1=1, 1-> OUT1=0 +MCR_OUT2 = 0x08 ; 0-> OUT2=1, 1-> OUT2=0; enable intr +MCR_LOOP = 0x10 ; lopback mode + +MSR_DCTS = 0x01 ; delta clear to send +MSR_DDSR = 0x02 ; delta data set redy +MSR_TERI = 0x04 ; trailinh edge of ring +MSR_DDCD = 0x08 ; delta carrier detect +MSR_CTS = 0x10 +MSR_DSR = 0x20 +MSR_RI = 0x40 +MSR_DCD = 0x80 + +MCR_TEST_MASK = MCR_DTR or MCR_RTS or MCR_OUT1 or MCR_OUT2 or MCR_LOOP +MSR_CHECK_MASK = MSR_CTS or MSR_DSR or MSR_RI or MSR_DCD + +struct DRV_DATA + io_addr dd ? ; base address of io port + port dd ? ; serial port descriptor +ends + +; dx = base io +; al = result +macro rd_reg reg +{ + push edx + add dx, reg + in al, dx + pop edx +} + +; dx = base io +; al = new value +macro wr_reg reg +{ + push edx + add dx, reg + out dx, al + pop edx +} + +; dx = port +; ax = divisor value +proc uart_set_baud + push eax + rd_reg LCR_REG + or al, LCR_DLAB + wr_reg LCR_REG + pop eax + wr_reg DLL_REG + shr ax, 8 + wr_reg DLM_REG + rd_reg LCR_REG + and al, 0x7f + wr_reg LCR_REG + ret +endp + +proc uart_probe stdcall uses ebx esi edi, io_addr:dword, irqn:dword + xor ebx, ebx ; 0 = reserve + mov ecx, [io_addr] + lea edx, [ecx + 7] + push ebp + invoke ReservePortArea + pop ebp + test eax, eax + jnz .err + + mov edx, [io_addr] + + ; enable loopback + mov al, MCR_LOOP + wr_reg MCR_REG + + ; read status + rd_reg MSR_REG + and al, MSR_CHECK_MASK + test al, al + jnz .free_port + + ; set test signals + mov al, MCR_TEST_MASK + wr_reg MCR_REG + + ; check signals + rd_reg MSR_REG + and al, MSR_CHECK_MASK + cmp al, MSR_CHECK_MASK + jnz .free_port + + DEBUGF L_DBG, "uart16550: found serial port %x\n", [io_addr] + + ; initialize port + xor ax, ax + wr_reg MCR_REG + wr_reg IER_REG + wr_reg LCR_REG + wr_reg FCR_REG + + mov eax, sizeof.DRV_DATA + invoke Kmalloc + test eax, eax + jz .free_port + mov edi, eax + + mov eax, [io_addr] + mov [edi + DRV_DATA.io_addr], eax + + invoke AttachIntHandler, [irqn], uart_int_handler, edi + test eax, eax + jz .free_desc + + ; register port + lea ecx, [uart_drv] + mov edx, edi + call add_port + test eax, eax + jz .free_desc ; TODO detach_int_handler? + + ; save port handle + mov [edi + DRV_DATA.port], eax + ret + + .free_desc: + mov eax, edi + invoke Kfree + + .free_port: + xor ebx, ebx + inc ebx ; 1 = release + mov ecx, [io_addr] + lea edx, [ecx + 7] + push ebp + invoke ReservePortArea + pop ebp + + .err: + ret +endp + +proc uart_int_handler c uses ebx esi edi, data:dword +locals + .buf db ? +endl + mov edi, [data] + mov edx, [edi + DRV_DATA.io_addr] + xor ebx, ebx + + .read_iir: + rd_reg IIR_REG + test al, IIR_INTR + jnz .exit + + inc ebx + and ax, IIR_IID + shr ax, 1 + + ; check source + test ax, ax + jz .modem + cmp ax, 1 + jz .xmit + cmp ax, 2 + jz .recv + cmp ax, 3 + jz .status + jmp .exit + + .modem: + ; read MSR for clear interrupt + rd_reg MSR_REG + jmp .read_iir + + .xmit: + push edx + mov eax, [edi + DRV_DATA.port] + mov edx, SERIAL_EVT_TXE + mov ecx, 1 + lea esi, [.buf] + call handle_event + pop edx + + test eax, eax + jz .no_data + + mov al, [.buf] + wr_reg THR_REG + jmp .read_iir + + .no_data: + ; disable THR empty interrupt + rd_reg IER_REG + and ax, not IER_THRI + wr_reg IER_REG + jmp .read_iir + + .recv: + ; read byte + rd_reg THR_REG + push edx + mov [.buf], al + mov eax, [edi + DRV_DATA.port] + mov edx, SERIAL_EVT_RXNE + mov ecx, 1 + lea esi, [.buf] + call handle_event + pop edx + + ; check for more recevied bytes + rd_reg LSR_REG + test al, LSR_DR + jnz .recv + jmp .read_iir + + .status: + rd_reg LSR_REG + jmp .read_iir + + .fifo: + jmp .read_iir + + .exit: + xchg eax, ebx + ret +endp + +proc uart_startup stdcall, data:dword, conf:dword + DEBUGF L_DBG, "uart16550: startup %x %x\n", [data], [conf] + ; enable and reset fifo, 1 byte trigger level + mov ecx, [data] + mov edx, [ecx + DRV_DATA.io_addr] + mov ax, FCR_EFIFO or FCR_CRB or FCR_CXMIT + wr_reg FCR_REG + ; configure at startup + stdcall uart_reconf, [data], [conf] + ret +endp + +proc uart_shutdown stdcall, data:dword + DEBUGF L_DBG, "uart16550: shutdown %x\n", [data] + ; disable interrupts + mov ecx, [data] + mov edx, [ecx + DRV_DATA.io_addr] + xor ax, ax + wr_reg IER_REG + ret +endp + +proc uart_reconf stdcall uses ebx esi, data:dword, conf:dword +locals + divisor dw ? + lcr dw ? +endl + ; calc divisor = BASE_FREQ / BASE_DIV / baudrate + mov esi, [conf] + xor edx, edx + mov eax, BASE_FREQ / BASE_DIV + div [esi + SP_CONF.baudrate] + test edx, edx + jnz .fail + test eax, eax + jz .fail + mov [divisor], ax + + ; calc word size + xor eax, eax + mov al, [esi + SP_CONF.word_size] + cmp al, 8 + ja .fail + sub al, 5 + jb .fail + mov [lcr], ax + + ; calc parity + mov al, [esi + SP_CONF.parity] + xor bx, bx + cmp al, SERIAL_CONF_PARITY_NONE + je .parity_ok + or bl, LCR_PARITY + cmp al, SERIAL_CONF_PARITY_ODD + je .parity_ok + or bl, LCR_EVEN + cmp al, SERIAL_CONF_PARITY_EVEN + je .parity_ok + mov bl, LCR_STICK or LCR_PARITY + cmp al, SERIAL_CONF_PARITY_MARK + je .parity_ok + or bl, LCR_EVEN + cmp al, SERIAL_CONF_PARITY_SPACE + jne .fail + .parity_ok: + mov [lcr], bx + + ; calc stop bits + mov bx, LCR_STOP_1 + mov al, [esi + SP_CONF.stop_bits] + cmp al, 1 + je .stop_bits_ok + or bx, LCR_STOP_2 + cmp al, 2 + jne .fail + .stop_bits_ok: + or [lcr], bx + + mov esi, [data] + mov edx, [esi + DRV_DATA.io_addr] + + spin_lock_irqsave + rd_reg IER_REG + and ax, IER_RDAI or IER_LSI + wr_reg IER_REG + spin_unlock_irqrestore + + mov ax, [divisor] + call uart_set_baud + + mov bx, [lcr] + wr_reg LCR_REG + + mov al, MCR_DTR or MCR_OUT1 or MCR_OUT2 + wr_reg MCR_REG + + ; enable rx interrupt + mov al, IER_RDAI or IER_LSI + wr_reg IER_REG + + xor eax, eax + ret + .fail: + mov eax, SERIAL_API_ERR_CONF + ret +endp + +proc uart_tx stdcall, data:dword + mov ecx, [data] + mov edx, [ecx + DRV_DATA.io_addr] + spin_lock_irqsave + rd_reg IER_REG + or ax, IER_THRI + wr_reg IER_REG + spin_unlock_irqrestore + ret +endp + +align 4 +uart_drv: + dd uart_drv_end - uart_drv + dd uart_startup + dd uart_shutdown + dd uart_reconf + dd uart_tx +uart_drv_end: diff --git a/drivers/usb/usbftdi/usbftdi.asm b/drivers/usb/usbftdi/usbftdi.asm index 3eeffe8c4c..c7336994a6 100644 --- a/drivers/usb/usbftdi/usbftdi.asm +++ b/drivers/usb/usbftdi/usbftdi.asm @@ -27,6 +27,7 @@ include '../../proc32.inc' include '../../peimport.inc' include '../../fdo.inc' include '../../struct.inc' +include '../../serial/common.inc' ; USB constants DEVICE_DESCR_TYPE = 1 @@ -60,6 +61,9 @@ LIBUSB_RECIPIENT_OTHER = 0x03 LIBUSB_ENDPOINT_IN = 0x80 LIBUSB_ENDPOINT_OUT = 0x00 +H_CLK = 120000000 +C_CLK = 48000000 + ; FTDI Constants FTDI_DEVICE_OUT_REQTYPE = (LIBUSB_REQUEST_TYPE_VENDOR or LIBUSB_RECIPIENT_DEVICE or LIBUSB_ENDPOINT_OUT) FTDI_DEVICE_IN_REQTYPE = (LIBUSB_REQUEST_TYPE_VENDOR or LIBUSB_RECIPIENT_DEVICE or LIBUSB_ENDPOINT_IN) @@ -119,6 +123,7 @@ TYPE_230X=7 ;strings my_driver db 'usbother',0 +serial_driver db 'SERIAL',0 nomemory_msg db 'K : no memory',13,10,0 ; Structures @@ -130,8 +135,8 @@ readBufChunkSize dd ? writeBufChunkSize dd ? readBufPtr dd ? writeBufPtr dd ? -readBufSize dd ? -writeBufSize dd ? +readBufLock dd ? +writeBufLock dd ? maxPacketSize dd ? interface dd ? index dd ? @@ -140,6 +145,8 @@ outEP dd ? nullP dd ? lockPID dd ? next_context dd ? +port_handle dd ? +rx_timer dd ? ends struct IOCTL @@ -182,7 +189,9 @@ proc START c, .reason:DWORD, .cmdline:DWORD xor eax, eax ; initialize return value cmp [.reason], 1 ; compare the argument - jnz .nothing + jnz .nothing + invoke GetService, serial_driver + mov [serial_drv_entry], eax invoke RegUSBDriver, my_driver, service_proc, usb_functions .nothing: @@ -215,6 +224,10 @@ proc AddDevice stdcall uses ebx esi edi, .config_pipe:DWORD, .config_descr:DWORD mov [eax + ftdi_context.maxPacketSize], 64 mov [eax + ftdi_context.readBufChunkSize], 64 mov [eax + ftdi_context.writeBufChunkSize], 64 + mov [eax + ftdi_context.readBufPtr], 0 + mov [eax + ftdi_context.writeBufPtr], 0 + mov [eax + ftdi_context.readBufLock], 0 + mov [eax + ftdi_context.writeBufLock], 0 mov [eax + ftdi_context.chipType], TYPE_R jmp .slow @@ -270,6 +283,15 @@ proc AddDevice stdcall uses ebx esi edi, .config_pipe:DWORD, .config_descr:DWORD test eax, eax jz .nothing mov [ebx + ftdi_context.inEP], eax + + mov eax, [serial_drv_entry] + test eax, eax + jz @f + stdcall serial_add_port, uart_drv, ebx + DEBUGF 1, "usbftdi: add serial port with result %x\n", eax + @@: + mov [ebx + ftdi_context.port_handle], eax + mov eax, ebx ret @@ -1013,12 +1035,340 @@ endp proc DeviceDisconnected stdcall uses ebx esi edi, .device_data:DWORD DEBUGF 1, 'K : FTDI deleting device data 0x%x\n', [.device_data] - mov eax, [.device_data] + mov esi, [.device_data] + mov eax, [esi + ftdi_context.readBufPtr] + test eax, eax + jz @f + invoke Kfree + @@: + mov eax, [esi + ftdi_context.writeBufPtr] + test eax, eax + jz @f + invoke Kfree + @@: + mov eax, [esi + ftdi_context.port_handle] + test eax, eax + jz @f + stdcall serial_remove_port, eax + @@: + mov eax, esi call linkedlist_unlink invoke Kfree ret endp +proc bulk_in_complete stdcall uses ebx edi esi, .pipe:DWORD, .status:DWORD, \ + .buffer:DWORD, .length:DWORD, .calldata:DWORD + mov eax, [.status] + test eax, eax + jz @f + DEBUGF 2, 'ftdi: bulk in error %x\n', eax + @@: + mov ebx, [.calldata] + btr dword [ebx + ftdi_context.writeBufLock], 0 + stdcall uart_tx, ebx + ret +endp + +proc bulk_out_complete stdcall uses ebx edi esi, .pipe:DWORD, .status:DWORD, \ + .buffer:DWORD, .length:DWORD, .calldata:DWORD + mov eax, [.status] + test eax, eax + jz @f + DEBUGF 2, 'ftdi: bulk out error %x\n', eax + @@: + mov ebx, [.calldata] + mov ecx, [.length] + cmp ecx, 2 + jb @f + mov esi, [.buffer] + add esi, 2 + sub ecx, 2 + stdcall serial_handle_event, [ebx + ftdi_context.port_handle], \ + SERIAL_EVT_RXNE, ecx, esi + @@: + btr dword [ebx + ftdi_context.readBufLock], 0 + ret +endp + +proc uart_startup stdcall uses ebx, data:dword, conf:dword + DEBUGF 1, "ftdi: startup %x %x\n", [data], [conf] + stdcall uart_reconf, [data], [conf] + test eax, eax + jz @f + DEBUGF 2, "ftdi: uart reconf error %x\n", eax + jmp .exit + @@: + mov ebx, [data] + invoke TimerHS, 2, 2, uart_rx, ebx + test eax, eax + jnz @f + DEBUGF 2, "ftdi: timer creation error\n" + or eax, -1 + jmp .exit + @@: + mov [ebx + ftdi_context.rx_timer], eax + xor eax, eax + .exit: + ret +endp + +proc uart_shutdown stdcall uses ebx, data:dword + DEBUGF 1, "ftdi: shutdown %x\n", [data] + mov ebx, [data] + cmp [ebx + ftdi_context.rx_timer], 0 + jz @f + invoke CancelTimerHS, [ebx + ftdi_context.rx_timer] + @@: + ret +endp + +proc uart_reconf stdcall uses ebx esi, dev:dword, conf:dword + mov ebx, [dev] + mov esi, [conf] + stdcall ftdi_set_baudrate, ebx, [esi + SP_CONF.baudrate] + ; TODO set word_size, parity, etc. + ret +endp + +proc uart_tx stdcall uses ebx esi, data:dword + xor eax, eax + mov ebx, [data] + bts dword [ebx + ftdi_context.writeBufLock], 0 + jc .exit + + mov esi, [ebx + ftdi_context.writeBufPtr] + test esi, esi + jnz .read + + ; allocate buffer for bulk_in transfer if not yet + mov eax, [ebx + ftdi_context.writeBufChunkSize] + invoke Kmalloc + test eax, eax + jz .error + mov [ebx + ftdi_context.writeBufPtr], eax + mov esi, eax + + .read: + mov eax, [ebx + ftdi_context.writeBufChunkSize] + stdcall serial_handle_event, [ebx + ftdi_context.port_handle], \ + SERIAL_EVT_TXE, eax, esi + test eax, eax + jz .unlock + invoke USBNormalTransferAsync, [ebx + ftdi_context.inEP], esi, eax, \ + bulk_in_complete, ebx, 1 + test eax, eax + jz .error + xor eax, eax + jmp .exit + + .error: + or eax, -1 + .unlock: + btr dword [ebx + ftdi_context.writeBufLock], 0 + + .exit: + ret +endp + +proc uart_rx stdcall uses ebx esi, data:dword + xor eax, eax + mov ebx, [data] + bts dword [ebx + ftdi_context.readBufLock], 0 + jc .exit + + mov esi, [ebx + ftdi_context.readBufPtr] + test esi, esi + jnz .read + + ; allocate buffer for bulk_out transfer if not yet + mov eax, [ebx + ftdi_context.readBufChunkSize] + invoke Kmalloc + test eax, eax + jz .error + mov [ebx + ftdi_context.readBufPtr], eax + mov esi, eax + + .read: + mov edx, [ebx + ftdi_context.readBufChunkSize] + invoke USBNormalTransferAsync, [ebx + ftdi_context.outEP], esi, edx, \ + bulk_out_complete, ebx, 1 + test eax, eax + jz .error + xor eax, eax + jmp .exit + .error: + btr dword [ebx + ftdi_context.readBufLock], 0 + or eax, -1 + .exit: + ret +endp + +proc ftdi_set_baudrate stdcall uses ebx esi edi, dev:dword, baud:dword +locals +ConfPacket rb 10 +EventData rd 3 +endl + xor esi, esi + xor ecx, ecx + invoke CreateEvent + mov [EventData], eax + mov [EventData + 4], edx + + mov ebx, [dev] + cmp [ebx + ftdi_context.chipType], TYPE_2232H + jl .c_clk + imul eax, [baud], 10 + cmp eax, H_CLK / 0x3FFF + jle .c_clk + .h_clk: + cmp dword [baud], H_CLK / 10 + jl .h_nextbaud1 + xor edx, edx + mov ecx, H_CLK / 10 + jmp .calcend + + .c_clk: + cmp dword [baud], C_CLK / 16 + jl .c_nextbaud1 + xor edx, edx + mov ecx, C_CLK / 16 + jmp .calcend + + .h_nextbaud1: + cmp dword [baud], H_CLK / (10 + 10 / 2) + jl .h_nextbaud2 + mov edx, 1 + mov ecx, H_CLK / (10 + 10 / 2) + jmp .calcend + + .c_nextbaud1: + cmp dword [baud], C_CLK / (16 + 16 / 2) + jl .c_nextbaud2 + mov edx, 1 + mov ecx, C_CLK/(16 + 16 / 2) + jmp .calcend + + .h_nextbaud2: + cmp dword [baud], H_CLK / (2 * 10) + jl .h_nextbaud3 + mov edx, 2 + mov ecx, H_CLK / (2 * 10) + jmp .calcend + + .c_nextbaud2: + cmp dword [edi + 8], C_CLK / (2 * 16) + jl .c_nextbaud3 + mov edx, 2 + mov ecx, C_CLK / (2 * 16) + jmp .calcend + + .h_nextbaud3: + mov eax, H_CLK * 16 / 10 ; eax - best_divisor + xor edx, edx + div dword [baud] + push eax + and eax, 1 + pop eax + shr eax, 1 + jz .h_rounddowndiv ; jump by result of and eax, 1 + inc eax + .h_rounddowndiv: + cmp eax, 0x20000 + jle .h_best_divok + mov eax, 0x1FFFF + .h_best_divok: + mov ecx, eax + mov eax, H_CLK * 16 / 10 + xor edx, edx + div ecx + xchg ecx, eax ; ecx - best_baud + push ecx + and ecx, 1 + pop ecx + shr ecx, 1 + jz .rounddownbaud + inc ecx + jmp .rounddownbaud + + .c_nextbaud3: + mov eax, C_CLK ; eax - best_divisor + xor edx, edx + div dword [baud] + push eax + and eax, 1 + pop eax + shr eax, 1 + jnz .c_rounddowndiv ; jump by result of and eax, 1 + inc eax + .c_rounddowndiv: + cmp eax, 0x20000 + jle .c_best_divok + mov eax, 0x1FFFF + .c_best_divok: + mov ecx, eax + mov eax, C_CLK + xor edx, edx + div ecx + xchg ecx, eax ; ecx - best_baud + push ecx + and ecx, 1 + pop ecx + shr ecx, 1 + jnz .rounddownbaud + inc ecx + + .rounddownbaud: + mov edx, eax ; edx - encoded_divisor + shr edx, 3 + and eax, 0x7 + push 7 6 5 1 4 2 3 0 + mov eax, [esp + eax * 4] + shl eax, 14 + or edx, eax + add esp, 32 + + .calcend: + mov eax, edx ; eax - *value + mov ecx, edx ; ecx - *index + and eax, 0xFFFF + cmp [ebx + ftdi_context.chipType], TYPE_2232H + jge .foxyindex + shr ecx, 16 + jmp .preparepacket + .foxyindex: + shr ecx, 8 + and ecx, 0xFF00 + or ecx, [ebx + ftdi_context.index] + + .preparepacket: + mov word [ConfPacket], (FTDI_DEVICE_OUT_REQTYPE) \ + + (SIO_SET_BAUDRATE_REQUEST shl 8) + mov word [ConfPacket + 2], ax + mov word [ConfPacket + 4], cx + mov word [ConfPacket + 6], 0 + + lea esi, [ConfPacket] + lea edi, [EventData] + invoke USBControlTransferAsync, [ebx + ftdi_context.nullP], esi, 0,\ + 0, control_callback, edi, 0 + test eax, eax + jz .error + mov eax, [EventData] + mov ebx, [EventData + 4] + invoke WaitEvent + + mov eax, [EventData] + mov ebx, [EventData + 4] + invoke DestroyEvent + xor eax, eax + ret + + .error: + or eax, -1 + ret +endp + include 'linkedlist.inc' align 4 @@ -1029,6 +1379,17 @@ usb_functions: dd AddDevice dd DeviceDisconnected +align 4 +uart_drv: + dd uart_drv_end - uart_drv + dd uart_startup + dd uart_shutdown + dd uart_reconf + dd uart_tx +uart_drv_end: + +serial_drv_entry dd 0 + data fixups end data