📄 edb7312-usb.c
字号:
return; if (p->fsm_dev < FSM_DEV_DEFAULT) goto err; drq = &p->drq; if (stat & D_READ_LTRNS_SETUP) { /* new transfer */ len = d12_buf_read(p, EPX_CTRL_O, drq, sizeof(struct devreq)); if (len != sizeof(struct devreq)) goto err; d12_acknowledge_setup(p); d12_select_endpoint(p, EPX_CTRL_O); d12_buf_clear(p); req = drq->bRequest; switch (drq->bmRequestType & REQTYPE_TYPE_M) { case REQTYPE_TYPE_STANDARD: if (req >= MAX_REQ_STANDARD) goto err; if ((T_STD_SETUP[req])(p)) goto err; break; case REQTYPE_TYPE_CLASS: if (p->fsm_dev != FSM_DEV_CONFIGURED) goto err; req = 255 - req; if (req >= MAX_REQ_CLASS) goto err; if ((T_CLS_SETUP[req])(p)) goto err; break; default: goto err; } } else { /* host -> d12 data */ switch (p->fsm_ctl) { case FSM_CTL_STATUS_OUT: d12_stall_ctrl(p, 1); p->fsm_ctl = FSM_CTL_IDLE; break; default: goto err; } } return;err: d12_stall_ctrl(p, 1); /* stall EP0 */ p->fsm_ctl = FSM_CTL_IDLE;}static void ep0_tx(struct d12 *p){ struct devreq *drq; int stat, len; stat = d12_read_last_trans(p, EPX_CTRL_I); if (!(stat & D_READ_LTRNS_SUCCESS)) return; if (p->fsm_dev < FSM_DEV_DEFAULT) goto err; drq = &p->drq; switch (p->fsm_ctl) { case FSM_CTL_DATA_IN: len = p->dlen - p->dptr; if (len > EP_CTRL_SIZE) len = EP_CTRL_SIZE; d12_buf_write(p, EPX_CTRL_I, p->dbuf + p->dptr, len); p->dptr += len; if (p->dptr == p->dlen) p->fsm_ctl = FSM_CTL_STATUS_OUT; p->fsm_ctl_lastpkt = len; break; case FSM_CTL_STATUS_OUT: if (p->fsm_ctl_lastpkt == EP_CTRL_SIZE) { d12_buf_write(p, EPX_CTRL_I, 0, 0); p->fsm_ctl_lastpkt = 0; } break; case FSM_CTL_STATUS_IN: d12_stall_ctrl(p, 1); p->fsm_ctl = FSM_CTL_IDLE; break; default: goto err; } return;err: d12_stall_ctrl(p, 1); /* stall EP0 */ p->fsm_ctl = FSM_CTL_IDLE;}static void ep1_rx(struct d12 *p) /* free endpoint */{ d12_read_last_trans(p, EPX_FREE_O); d12_stall_free(p, 1); /* stall EP1 */}static void ep1_tx(struct d12 *p) /* free endpoint */{ d12_read_last_trans(p, EPX_FREE_I); d12_stall_free(p, 1); /* stall EP1 */}static void ep2_rx(struct d12 *p){ struct cbw *cbw; struct csw *csw; int stat, len, lun, clen, cmd, r; stat = d12_read_last_trans(p, EPX_BULK_O); if (!(stat & D_READ_LTRNS_SUCCESS)) return; if (p->fsm_dev != FSM_DEV_CONFIGURED) goto err; cbw = &p->cbw; csw = &p->csw; switch (p->fsm_bulk) { case FSM_BULK_IDLE: len = d12_buf_read(p, EPX_BULK_O, cbw, sizeof(struct cbw)); if (len != sizeof(struct cbw)) goto err; lun = cbw->bCBWLUN; clen = cbw->bCBWCBLength; cmd = cbw->CBWCB[0]; if ((cbw->dCBWSignature != CBW_SIGNATURE) || !clen || (clen > sizeof(cbw->CBWCB))) goto err; p->bhd = 0; p->bxferlen = p->bxferptr = 0; p->cswdelay = p->cswearly = 0; csw->dCSWSignature = CSW_SIGNATURE; csw->dCSWTag = cbw->dCBWTag; csw->dCSWDataResidue = 0; csw->bCSWStatus = CSW_STAT_OK; if (lun >= p->max_lun) { csw->bCSWStatus = CSW_STAT_FAILED; scsi_sense_set(p, SCSI_SKEY_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_UNIT_NOT_SUPPORTED); bulk_xfer(p, BULK_NONE, 0, 0); } else if ((!T_SCSI_HD[cmd].len || (T_SCSI_HD[cmd].len == clen)) && T_SCSI_HD[cmd].hd) { r = T_SCSI_HD[cmd].hd(p, lun); if (r && (r != SCHD_ERR_PHASE)) { csw->bCSWStatus = CSW_STAT_FAILED; if (r == SCHD_ERR_INVALID_CDB) scsi_sense_set(p, SCSI_SKEY_ILLEGAL_REQUEST, SCSI_ASC_INVALID_FIELD_IN_CDB); else if (r == SCHD_ERR_INVALID_LBA) scsi_sense_set(p, SCSI_SKEY_ILLEGAL_REQUEST, SCSI_ASC_LBA_OUT_OF_RANGE); bulk_xfer(p, BULK_NONE, 0, 0); } } else { csw->bCSWStatus = CSW_STAT_FAILED; scsi_sense_set(p, SCSI_SKEY_ILLEGAL_REQUEST, SCSI_ASC_INVALID_CMD); bulk_xfer(p, BULK_NONE, 0, 0); } if (!p->bxferlen && !p->cswdelay) csw_write(p); break; case FSM_BULK_DATA_OUT: len = p->blen - p->bptr; len = d12_buf_read(p, EPX_BULK_O, p->bbuf + p->bptr, len); p->bptr += len; p->bxferptr += len; if (p->bptr == p->blen) { lun = cbw->bCBWLUN; if (p->bhd) p->bhd(p, lun, p->blen); p->fsm_bulk = FSM_BULK_DATA_OUT_WAIT; } break; case FSM_BULK_DATA_OUT_WAIT: break; default: goto err; } return;err: d12_stall_bulk(p, 1); /* stall EP2 */ p->fsm_bulk = FSM_BULK_IDLE;}static void ep2_tx(struct d12 *p){ struct cbw *cbw; int stat, len, lun; stat = d12_read_last_trans(p, EPX_BULK_I); if (!(stat & D_READ_LTRNS_SUCCESS)) return; if (p->fsm_dev != FSM_DEV_CONFIGURED) goto err; cbw = &p->cbw; switch (p->fsm_bulk) { case FSM_BULK_IDLE: /* CSW ack */ break; case FSM_BULK_DATA_IN: len = p->blen - p->bptr; if (len) { len = d12_buf_write(p, EPX_BULK_I, p->bbuf + p->bptr, len); p->bptr += len; p->bxferptr += len; } else { stat = d12_status_get(p, EPX_BULK_I); stat &= D_READ_ENDP_STAT_BUF0 | D_READ_ENDP_STAT_BUF1; if (!stat) { if ((p->bxferptr == p->bxferlen) || p->cswearly) csw_write(p); else { p->fsm_bulk = FSM_BULK_DATA_IN_WAIT; lun = cbw->bCBWLUN; if (p->bhd) p->bhd(p, lun, p->blen); } } } break; default: goto err; } return;err: d12_stall_bulk(p, 1); /* stall EP2 */ p->fsm_bulk = FSM_BULK_IDLE;}static void usbd12_interrupt(int irq, void *dev_id, struct pt_regs *regs){ struct d12 *p; int ints; p = (struct d12 *)dev_id; ints = d12_in2(p, CMD_READ_INT); if (ints & D_READ_INT_BUSRESET) { d12_reset(p); p->feat_wakeup = 0; p->fsm_dev = FSM_DEV_DEFAULT; p->fsm_ctl = FSM_CTL_IDLE; } if (ints & D_READ_INT_SUSPENDCHG) p->suspend = GPIO_D12_SUSP; if (ints & D_READ_INT_EP0_I) /* host <- d12 ack, control */ ep0_tx(p); if (ints & D_READ_INT_EP0_O) /* host -> d12, control */ ep0_rx(p); if (ints & D_READ_INT_EP1_I) /* host <- d12 ack, bulk */ ep1_tx(p); if (ints & D_READ_INT_EP1_O) /* host -> d12, bulk */ ep1_rx(p); if (ints & D_READ_INT_EP2_I) /* host <- d12 ack, bulk */ ep2_tx(p); if (ints & D_READ_INT_EP2_O) /* host -> d12, bulk */ ep2_rx(p);}/* ------------------------------------------------------------------------- *//* * device I/O thread */static int dio_thread(void *arg){ struct d12 *p; volatile struct dioreq *dio; int cmd, lun; __u32 lba, blks, nblks, i; unsigned char *buf; struct buffer_head *bh; p = (struct d12 *)arg; dio = &p->dio; daemonize(); reparent_to_init(); strcpy(current->comm, "kusbd12"); current->nice = -20;#if 0 spin_lock_irq(¤t->sigmask_lock); sigemptyset(¤t->blocked); recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock);#endif complete(&p->cpl_dio_sync); /* initialized */ for ( ; ; ) { down(&p->wait_dio_cmd);#if 0 if (signal_pending(current)) { spin_lock_irq(¤t->sigmask_lock); flush_signals(current); spin_unlock_irq(¤t->sigmask_lock); }#endif d12_lock(p); cmd = dio->cmd; lun = dio->lun; lba = dio->lba; blks = dio->blks; nblks = dio->nblks; buf = (unsigned char *)dio->buf; d12_unlock(p); if (cmd == DIO_CMD_EXIT) break; switch (cmd) { case DIO_CMD_READ: for (i = 0 ; i < blks ; i++) { bh = bread(p->atd_dev[lun], lba, p->atd_blk[lun]); if (!bh) break; memcpy(buf + i * p->atd_blk[lun], bh->b_data, p->atd_blk[lun]); brelse(bh); lba++; } d12_lock(p); ep_bulk_write(p, buf, i * p->atd_blk[lun]); if (i != blks) { scsi_sense_lba_set(p, lba, SCSI_ASC_UNRECOVERED_READ_ERROR); p->cswearly = 1; } d12_unlock(p); break; case DIO_CMD_WRITE: for (i = 0 ; i < blks ; i++) { bh = getblk(p->atd_dev[lun], lba, p->atd_blk[lun]); if (!bh) break; memcpy(bh->b_data, buf + i * p->atd_blk[lun], p->atd_blk[lun]); mark_buffer_uptodate(bh, 1); mark_buffer_dirty(bh); ll_rw_block(WRITE, 1, &bh); wait_on_buffer(bh); brelse(bh); lba++; } d12_lock(p); if (i != blks) { scsi_sense_lba_set(p, lba, SCSI_ASC_WRITE_ERROR); csw_write(p); } else if (nblks) ep_bulk_read(p, buf, nblks * p->atd_blk[lun]); else csw_write(p); d12_unlock(p); break; case DIO_CMD_VERIFY: for (i = 0 ; i < blks ; i++) { bh = bread(p->atd_dev[lun], lba, p->atd_blk[lun]); if (!bh) goto vfyerr; brelse(bh); lba++; }vfyerr: d12_lock(p); if (i != blks) scsi_sense_lba_set(p, lba, SCSI_ASC_UNRECOVERED_READ_ERROR); csw_write(p); d12_unlock(p); break; } } complete_and_exit(&p->cpl_dio_sync, 0);}/* ------------------------------------------------------------------------- */#define w(x) ((x) & 0xFF), (((x) >> 8) & 0xFF)#define u(x) (x), 0#define MAX_DESC_STR 6#define STR_MANUFACTURER 1#define STR_PRODUCT 2#define STR_SERIALNUM 3#define STR_CONFIG 4#define STR_INTERFACE 5static const unsigned char T_DESC_STR_0[] = { 4, /* bLength */ DESC_TYPE_STRING, /* bDescriptorType */ w(DEF_LANG) /* (english, US) */};static const unsigned char T_DESC_STR_1[] = { 26, /* bLength */ DESC_TYPE_STRING, /* bDescriptorType */ u('C'), /* (manufacturer) */ u('i'), u('r'), u('r'), u('u'), u('s'), u(' '), u('L'), u('o'), u('g'), u('i'), u('c')};static const unsigned char T_DESC_STR_2[] = { 16, /* bLength */ DESC_TYPE_STRING, /* bDescriptorType */ u('e'), /* (product) */ u('d'), u('b'), u('7'), u('3'), u('1'), u('2')};static const unsigned char T_DESC_STR_3[] = { 26, /* bLength */ DESC_TYPE_STRING, /* bDescriptorType */ u('0'), /* (serial number) */ u('0'), u('0'), u('0'), u('0'), u('0'), u('0'), u('0'), u('0'), u('0'), u('0'), u('0')};static const unsigned char T_DESC_STR_4[] = { 10, /* bLength */ DESC_TYPE_STRING, /* bDescriptorType */ u('c'), /* (configuration) */ u('f'), u('g'), u('0')};static const unsigned char T_DESC_STR_5[] = { 8, /* bLength */ DESC_TYPE_STRING, /* bDescriptorType */ u('i'), /* (interface) */ u('f'), u('0')};static const void * const T_DESC_STR[MAX_DESC_STR] = { T_DESC_STR_0, T_DESC_STR_1, T_DESC_STR_2, T_DESC_STR_3, T_DESC_STR_4, T_DESC_STR_5};static const int T_DESC_STR_LEN[MAX_DESC_STR] = { sizeof(T_DESC_STR_0), sizeof(T_DESC_STR_1), sizeof(T_DESC_STR_2), sizeof(T_DESC_STR_3), sizeof(T_DESC_STR_4), sizeof(T_DESC_STR_5)};static const unsigned char T_DESC_DEV[] = { /* --- device descriptor --- */ 18, /* bLength */ DESC_TYPE_DEVICE, /* bDescriptorType */ 0x01, 0x01, /* bcdUSB */ 0, /* bDeviceClass */ 0, /* bDeviceSubClass */ 0, /* bDeviceProtocol */ EP_CTRL_SIZE, /* bMaxPacketSize0 */ 0xDB, 0xCE, /* idVendor */ 0x12, 0x73, /* idProduct */ 0x00, 0x01, /* bcdDevice */ STR_MANUFACTURER, /* iManufacturer */ STR_PRODUCT, /* iProduct */ STR_SERIALNUM, /* iSerialNumber */ 1 /* bNumConfigurations */};static const unsigned char T_DESC_CFG[] = { /* --- configuration descriptor --- */ 9, /* bLength */ DESC_TYPE_CONFIGURATION, /* bDescriptorType */ w(9 + 9 + 7 + 7), /* wTotalLength */ 1, /* bNumInterfaces */ CONFIG_VAL, /* bConfigurationValue */ STR_CONFIG, /* iConfiguration */ 0xE0, /* bmAttributes */ 0, /* MaxPower (0 mA) */ /* --- interface descriptor --- */ 9, /* bLength */ DESC_TYPE_INTERFACE, /* bDescriptorType */ 0, /* bInterfaceNumber */ 0, /* bAlternateSetting */ 2, /* bNumEndpoints */ CLASS_MASS_STORAGE, /* bInterfaceClass (mass-storage) */ SUBCLASS_SCSI, /* bInterfaceSubClass (scsi) */ PROTOCOL_BULK, /* bInterfaceProtocol (bulk) */ STR_INTERFACE, /* iInterface */ /* --- out endpoint descriptor --- */ 7, /* bLength */ DESC_TYPE_ENDPOINT, /* bDescriptorType */ EP_BULK | 0x00, /* bEndpointAddress */ 0x02, /* bmAttributes (bulk) */ w(EP_BULK_SIZE), /* wMaxPacketSize */ 0, /* bInterval */ /* --- in endpoint descriptor --- */ 7, /* bLength */ DESC_TYPE_ENDPOINT, /* bDescriptorType */ EP_BULK | 0x80, /* bEndpointAddress */ 0x02, /* bmAttributes (bulk) */ w(EP_BULK_SIZE), /* wMaxPacketSize */ 0 /* bInterval */};#undef w#undef ustatic const char T_STR_SCSI_VENDOR[] = "CIRRUSL ";static const char T_STR_SCSI_PRODUCT[] = "EDB7312 ";static const char T_STR_SCSI_REVISION[] = "0000";/* --- */static int thread_init(struct d12 *p){ int r; init_completion(&p->cpl_dio_sync); sema_init(&p->wait_dio_cmd, 0); r = kernel_thread(dio_thread, p, 0); if (r < 0) { printk("%s: couldn't spawn thread\n", __FUNCTION__); return r; } wait_for_completion(&p->cpl_dio_sync); /* wait thread initialize */ return 0;}static void thread_exit(struct d12 *p){ p->dio.cmd = DIO_CMD_EXIT; up(&p->wait_dio_cmd); wait_for_completion(&p->cpl_dio_sync); /* wait thread exit */}static void dev_add(struct d12 *p, kdev_t dev){ int lun, maj, min; lun = p->max_lun; if (lun == 16) { printk("ignored, limit of 16 reached\n"); goto err0; } maj = MAJOR(dev); min = MINOR(dev); printk("%s: dev %u:%u - ", ID, maj, min); if (is_mounted(dev)) { printk("ignored, mounted\n"); goto err0; } p->bdev[lun] = bdget(dev); if (!p->bdev[lun]) { printk("ignored, no memory\n"); goto err0; } if (blkdev_get(p->bdev[lun], FMODE_READ | FMODE_WRITE, 0, BDEV_RAW)) { printk("ignored, blkdev_get() error\n"); goto err0; } p->scsi_str_vendor[lun] = T_STR_SCSI_VENDOR; p->scsi_str_product[lun] = T_STR_SCSI_PRODUCT; p->scsi_str_revision[lun] = T_STR_SCSI_REVISION; p->atd_dev[lun] = dev; p->atd_blk[lun] = get_hardsect_size(dev); if (!p->atd_blk[lun] || (p->atd_blk[lun] > MAX_CHUNKBUF)) { printk("ignored (bogus sector size %u)\n", p->atd_blk[lun]); goto err1; } if (set_blocksize(dev, p->atd_blk[lun])) { printk("ignored, set_blocksize() error\n"); goto err1; } p->atd_chunkblk[lun] = MAX_CHUNKBUF / p->atd_blk[lun]; p->atd_lba_capacity[lun] = 0; if (blk_size[maj]) p->atd_lba_capacity[lun] = blk_size[maj][min]; if (!p->atd_lba_capacity[lun]) { printk("ignored (zero size)\n"); goto err1; } p->atd_stat[lun] = ATD_STAT_READY; printk("added %s\n", (p->atd_stat[lun] & ATD_STAT_RO) ? "read-only" : "read-write"); p->max_lun++; return;err1: blkdev_put(p->bdev[lun], BDEV_RAW);err0: return;}static void dev_del(struct d12 *p){ int lun; for (lun = 0 ; lun < p->max_lun ; lun++) { blkdev_put(p->bdev[lun], BDEV_RAW); }}int __init usbd12_init_module(void){ struct d12 *p; int r = -EINVAL; printk("PDIUSBD12 / mass-storage support for EDB7312\n"); /* prepare context */ p = &d12; p->reg_cmd = REG_D12_CMD; p->reg_dat = REG_D12_DAT; p->irq = IRQ_D12; p->desc_dev = T_DESC_DEV; p->desc_dev_len = sizeof(T_DESC_DEV); p->desc_cfg = T_DESC_CFG; p->desc_cfg_len = sizeof(T_DESC_CFG); p->desc_str_max = MAX_DESC_STR; p->desc_str = T_DESC_STR; p->desc_str_len = T_DESC_STR_LEN; /* attach devices */ dev_add(p, MKDEV(1, 1)); /* /dev/ram1 */#if 0 dev_add(p, MKDEV(3, 1)); /* /dev/hda1 */ dev_add(p, MKDEV(3, 65)); /* /dev/hdb1 */#endif if (!p->max_lun) { printk("%s: no devices attached, skipping driver loading\n", ID); goto err0; } r = thread_init(p); if (r) goto err1; d12_init(p); p->suspend = GPIO_D12_SUSP; r = request_irq(p->irq, usbd12_interrupt, SA_SHIRQ, "pdiusbd12", p); if (r) { printk("%s: couldn't allocate IRQ %u\n", ID, p->irq); goto err2; } edb7312_enable_eint1(); return 0;err2: thread_exit(p);err1: dev_del(p);err0: return r;}static void __exit usbd12_exit_module(void){ struct d12 *p; p = &d12; free_irq(p->irq, p); thread_exit(p); dev_del(p);}module_init(usbd12_init_module);module_exit(usbd12_exit_module);MODULE_AUTHOR("Vladimir Ivanov <vladitx@nucleusys.com>");MODULE_DESCRIPTION("PDIUSBD12 / mass-storage support for EDB7312");MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -