📄 sisusb.c
字号:
/* Sanity check */ if (!sisusb || !sisusb->present || !sisusb->sisusb_dev) return -ENODEV; pipe = usb_rcvbulkpipe(sisusb->sisusb_dev, ep); buffer = sisusb->ibuf; bufsize = sisusb->ibufsize; retry = 5;#ifdef SISUSB_DONTSYNC if (!(sisusb_wait_all_out_complete(sisusb))) return -EIO;#endif while (count > 0) { if (!sisusb->sisusb_dev) return -ENODEV; thispass = (bufsize < count) ? bufsize : count; result = sisusb_bulkin_msg(sisusb, pipe, buffer, thispass, &transferred_len, 5 * HZ, tflags, sisusb->transfer_dma_in); if (transferred_len) thispass = transferred_len; else if (result == -ETIMEDOUT) { if (!retry--) return -ETIME; continue; } else return -EIO; if (thispass) { (*bytes_read) += thispass; count -= thispass; if (userbuffer) { if (copy_to_user(userbuffer, buffer, thispass)) return -EFAULT; userbuffer += thispass; } else { memcpy(kernbuffer, buffer, thispass); kernbuffer += thispass; } } } return ((*bytes_read) == len) ? 0 : -EIO;}static int sisusb_send_packet(struct sisusb_usb_data *sisusb, int len, struct sisusb_packet *packet){ int ret; ssize_t bytes_transferred = 0; __le32 tmp; if (len == 6) packet->data = 0;#ifdef SISUSB_DONTSYNC if (!(sisusb_wait_all_out_complete(sisusb))) return 1;#endif /* Eventually correct endianness */ SISUSB_CORRECT_ENDIANNESS_PACKET(packet); /* 1. send the packet */ ret = sisusb_send_bulk_msg(sisusb, SISUSB_EP_GFX_OUT, len, (char *)packet, NULL, 0, &bytes_transferred, 0, 0); if ((ret == 0) && (len == 6)) { /* 2. if packet len == 6, it means we read, so wait for 32bit * return value and write it to packet->data */ ret = sisusb_recv_bulk_msg(sisusb, SISUSB_EP_GFX_IN, 4, (char *)&tmp, NULL, &bytes_transferred, 0); packet->data = le32_to_cpu(tmp); } return ret;}static int sisusb_send_bridge_packet(struct sisusb_usb_data *sisusb, int len, struct sisusb_packet *packet, unsigned int tflags){ int ret; ssize_t bytes_transferred = 0; __le32 tmp; if (len == 6) packet->data = 0;#ifdef SISUSB_DONTSYNC if (!(sisusb_wait_all_out_complete(sisusb))) return 1;#endif /* Eventually correct endianness */ SISUSB_CORRECT_ENDIANNESS_PACKET(packet); /* 1. send the packet */ ret = sisusb_send_bulk_msg(sisusb, SISUSB_EP_BRIDGE_OUT, len, (char *)packet, NULL, 0, &bytes_transferred, tflags, 0); if ((ret == 0) && (len == 6)) { /* 2. if packet len == 6, it means we read, so wait for 32bit * return value and write it to packet->data */ ret = sisusb_recv_bulk_msg(sisusb, SISUSB_EP_BRIDGE_IN, 4, (char *)&tmp, NULL, &bytes_transferred, 0); packet->data = le32_to_cpu(tmp); } return ret;}/* access video memory and mmio (return 0 on success) *//* Low level *//* The following routines assume being used to transfer byte, word, * long etc. * This means that * - the write routines expect "data" in machine endianness format. * The data will be converted to leXX in sisusb_xxx_packet. * - the read routines can expect read data in machine-endianess. */static int sisusb_write_memio_byte(struct sisusb_usb_data *sisusb, int type, u32 addr, u8 data){ struct sisusb_packet packet; int ret; packet.header = (1 << (addr & 3)) | (type << 6); packet.address = addr & ~3; packet.data = data << ((addr & 3) << 3); ret = sisusb_send_packet(sisusb, 10, &packet); return ret;}static int sisusb_write_memio_word(struct sisusb_usb_data *sisusb, int type, u32 addr, u16 data){ struct sisusb_packet packet; int ret = 0; packet.address = addr & ~3; switch (addr & 3) { case 0: packet.header = (type << 6) | 0x0003; packet.data = (u32)data; ret = sisusb_send_packet(sisusb, 10, &packet); break; case 1: packet.header = (type << 6) | 0x0006; packet.data = (u32)data << 8; ret = sisusb_send_packet(sisusb, 10, &packet); break; case 2: packet.header = (type << 6) | 0x000c; packet.data = (u32)data << 16; ret = sisusb_send_packet(sisusb, 10, &packet); break; case 3: packet.header = (type << 6) | 0x0008; packet.data = (u32)data << 24; ret = sisusb_send_packet(sisusb, 10, &packet); packet.header = (type << 6) | 0x0001; packet.address = (addr & ~3) + 4; packet.data = (u32)data >> 8; ret |= sisusb_send_packet(sisusb, 10, &packet); } return ret;}static int sisusb_write_memio_24bit(struct sisusb_usb_data *sisusb, int type, u32 addr, u32 data){ struct sisusb_packet packet; int ret = 0; packet.address = addr & ~3; switch (addr & 3) { case 0: packet.header = (type << 6) | 0x0007; packet.data = data & 0x00ffffff; ret = sisusb_send_packet(sisusb, 10, &packet); break; case 1: packet.header = (type << 6) | 0x000e; packet.data = data << 8; ret = sisusb_send_packet(sisusb, 10, &packet); break; case 2: packet.header = (type << 6) | 0x000c; packet.data = data << 16; ret = sisusb_send_packet(sisusb, 10, &packet); packet.header = (type << 6) | 0x0001; packet.address = (addr & ~3) + 4; packet.data = (data >> 16) & 0x00ff; ret |= sisusb_send_packet(sisusb, 10, &packet); break; case 3: packet.header = (type << 6) | 0x0008; packet.data = data << 24; ret = sisusb_send_packet(sisusb, 10, &packet); packet.header = (type << 6) | 0x0003; packet.address = (addr & ~3) + 4; packet.data = (data >> 8) & 0xffff; ret |= sisusb_send_packet(sisusb, 10, &packet); } return ret;}static int sisusb_write_memio_long(struct sisusb_usb_data *sisusb, int type, u32 addr, u32 data){ struct sisusb_packet packet; int ret = 0; packet.address = addr & ~3; switch (addr & 3) { case 0: packet.header = (type << 6) | 0x000f; packet.data = data; ret = sisusb_send_packet(sisusb, 10, &packet); break; case 1: packet.header = (type << 6) | 0x000e; packet.data = data << 8; ret = sisusb_send_packet(sisusb, 10, &packet); packet.header = (type << 6) | 0x0001; packet.address = (addr & ~3) + 4; packet.data = data >> 24; ret |= sisusb_send_packet(sisusb, 10, &packet); break; case 2: packet.header = (type << 6) | 0x000c; packet.data = data << 16; ret = sisusb_send_packet(sisusb, 10, &packet); packet.header = (type << 6) | 0x0003; packet.address = (addr & ~3) + 4; packet.data = data >> 16; ret |= sisusb_send_packet(sisusb, 10, &packet); break; case 3: packet.header = (type << 6) | 0x0008; packet.data = data << 24; ret = sisusb_send_packet(sisusb, 10, &packet); packet.header = (type << 6) | 0x0007; packet.address = (addr & ~3) + 4; packet.data = data >> 8; ret |= sisusb_send_packet(sisusb, 10, &packet); } return ret;}/* The xxx_bulk routines copy a buffer of variable size. They treat the * buffer as chars, therefore lsb/msb has to be corrected if using the * byte/word/long/etc routines for speed-up * * If data is from userland, set "userbuffer" (and clear "kernbuffer"), * if data is in kernel space, set "kernbuffer" (and clear "userbuffer"); * if neither "kernbuffer" nor "userbuffer" are given, it is assumed * that the data already is in the transfer buffer "sisusb->obuf[index]". */static int sisusb_write_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr, char *kernbuffer, int length, const char __user *userbuffer, int index, ssize_t *bytes_written){ struct sisusb_packet packet; int ret = 0; static int msgcount = 0; u8 swap8, fromkern = kernbuffer ? 1 : 0; u16 swap16; u32 swap32, flag = (length >> 28) & 1; char buf[4]; /* if neither kernbuffer not userbuffer are given, assume * data in obuf */ if (!fromkern && !userbuffer) kernbuffer = sisusb->obuf[index]; (*bytes_written = 0); length &= 0x00ffffff; while (length) { switch (length) { case 0: return ret; case 1: if (userbuffer) { if (get_user(swap8, (u8 __user *)userbuffer)) return -EFAULT; } else swap8 = kernbuffer[0]; ret = sisusb_write_memio_byte(sisusb, SISUSB_TYPE_MEM, addr, swap8); if (!ret) (*bytes_written)++; return ret; case 2: if (userbuffer) { if (get_user(swap16, (u16 __user *)userbuffer)) return -EFAULT; } else swap16 = *((u16 *)kernbuffer); ret = sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM, addr, swap16); if (!ret) (*bytes_written) += 2; return ret; case 3: if (userbuffer) { if (copy_from_user(&buf, userbuffer, 3)) return -EFAULT;#ifdef __BIG_ENDIAN swap32 = (buf[0] << 16) | (buf[1] << 8) | buf[2];#else swap32 = (buf[2] << 16) | (buf[1] << 8) | buf[0];#endif } else#ifdef __BIG_ENDIAN swap32 = (kernbuffer[0] << 16) | (kernbuffer[1] << 8) | kernbuffer[2];#else swap32 = (kernbuffer[2] << 16) | (kernbuffer[1] << 8) | kernbuffer[0];#endif ret = sisusb_write_memio_24bit(sisusb, SISUSB_TYPE_MEM, addr, swap32); if (!ret) (*bytes_written) += 3; return ret; case 4: if (userbuffer) { if (get_user(swap32, (u32 __user *)userbuffer)) return -EFAULT; } else swap32 = *((u32 *)kernbuffer); ret = sisusb_write_memio_long(sisusb, SISUSB_TYPE_MEM, addr, swap32); if (!ret) (*bytes_written) += 4; return ret; default: if ((length & ~3) > 0x10000) { packet.header = 0x001f; packet.address = 0x000001d4; packet.data = addr; ret = sisusb_send_bridge_packet(sisusb, 10, &packet, 0); packet.header = 0x001f; packet.address = 0x000001d0; packet.data = (length & ~3); ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0); packet.header = 0x001f; packet.address = 0x000001c0; packet.data = flag | 0x16; ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0); if (userbuffer) { ret |= sisusb_send_bulk_msg(sisusb, SISUSB_EP_GFX_LBULK_OUT, (length & ~3), NULL, userbuffer, 0, bytes_written, 0, 1); userbuffer += (*bytes_written); } else if (fromkern) { ret |= sisusb_send_bulk_msg(sisusb, SISUSB_EP_GFX_LBULK_OUT, (length & ~3), kernbuffer, NULL, 0, bytes_written, 0, 1); kernbuffer += (*bytes_written); } else { ret |= sisusb_send_bulk_msg(sisusb, SISUSB_EP_GFX_LBULK_OUT, (length & ~3), NULL, NULL, index, bytes_written, 0, 1); kernbuffer += ((*bytes_written) & (sisusb->obufsize-1)); } } else { packet.header = 0x001f; packet.address = 0x00000194; packet.data = addr; ret = sisusb_send_bridge_packet(sisusb, 10, &packet, 0); packet.header = 0x001f; packet.address = 0x00000190; packet.data = (length & ~3); ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0); if (sisusb->flagb0 != 0x16) { packet.header = 0x001f; packet.address = 0x00000180; packet.data = flag | 0x16; ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0); sisusb->flagb0 = 0x16; } if (userbuffer) { ret |= sisusb_send_bulk_msg(sisusb, SISUSB_EP_GFX_BULK_OUT, (length & ~3), NULL, userbuffer, 0, bytes_written, 0, 1); userbuffer += (*bytes_written); } else if (fromkern) { ret |= sisusb_send_bulk_msg(sisusb, SISUSB_EP_GFX_BULK_OUT, (length & ~3), kernbuffer, NULL, 0, bytes_written, 0, 1); kernbuffer += (*bytes_written); } else { ret |= sisusb_send_bulk_msg(sisusb, SISUSB_EP_GFX_BULK_OUT, (length & ~3), NULL, NULL, index, bytes_written, 0, 1); kernbuffer += ((*bytes_written) & (sisusb->obufsize-1)); } } if (ret) { msgcount++; if (msgcount < 500) printk(KERN_ERR "sisusbvga[%d]: Wrote %zd of " "%d bytes, error %d\n", sisusb->minor, *bytes_written, length, ret); else if (msgcount == 500) printk(KERN_ERR "sisusbvga[%d]: Too many errors" ", logging stopped\n", sisusb->minor); } addr += (*bytes_written); length -= (*bytes_written); } if (ret) break; } return ret ? -EIO : 0;}/* Remember: Read data in packet is in machine-endianess! So for * byte, word, 24bit, long no endian correction is necessary. */static int sisusb_read_memio_byte(struct sisusb_usb_data *sisusb, int type, u32 addr, u8 *data){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -