18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for Rio Karma 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * (c) 2006 Bob Copeland <me@bobcopeland.com> 68c2ecf20Sopenharmony_ci * (c) 2006 Keith Bennett <keith@mcs.st-and.ac.uk> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/slab.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <scsi/scsi.h> 138c2ecf20Sopenharmony_ci#include <scsi/scsi_cmnd.h> 148c2ecf20Sopenharmony_ci#include <scsi/scsi_device.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include "usb.h" 178c2ecf20Sopenharmony_ci#include "transport.h" 188c2ecf20Sopenharmony_ci#include "debug.h" 198c2ecf20Sopenharmony_ci#include "scsiglue.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define DRV_NAME "ums-karma" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Driver for Rio Karma"); 248c2ecf20Sopenharmony_ciMODULE_AUTHOR("Bob Copeland <me@bobcopeland.com>, Keith Bennett <keith@mcs.st-and.ac.uk>"); 258c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 268c2ecf20Sopenharmony_ciMODULE_IMPORT_NS(USB_STORAGE); 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define RIO_PREFIX "RIOP\x00" 298c2ecf20Sopenharmony_ci#define RIO_PREFIX_LEN 5 308c2ecf20Sopenharmony_ci#define RIO_SEND_LEN 40 318c2ecf20Sopenharmony_ci#define RIO_RECV_LEN 0x200 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define RIO_ENTER_STORAGE 0x1 348c2ecf20Sopenharmony_ci#define RIO_LEAVE_STORAGE 0x2 358c2ecf20Sopenharmony_ci#define RIO_RESET 0xC 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistruct karma_data { 388c2ecf20Sopenharmony_ci int in_storage; 398c2ecf20Sopenharmony_ci char *recv; 408c2ecf20Sopenharmony_ci}; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic int rio_karma_init(struct us_data *us); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* 468c2ecf20Sopenharmony_ci * The table of devices 478c2ecf20Sopenharmony_ci */ 488c2ecf20Sopenharmony_ci#define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \ 498c2ecf20Sopenharmony_ci vendorName, productName, useProtocol, useTransport, \ 508c2ecf20Sopenharmony_ci initFunction, flags) \ 518c2ecf20Sopenharmony_ci{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ 528c2ecf20Sopenharmony_ci .driver_info = (flags) } 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic struct usb_device_id karma_usb_ids[] = { 558c2ecf20Sopenharmony_ci# include "unusual_karma.h" 568c2ecf20Sopenharmony_ci { } /* Terminating entry */ 578c2ecf20Sopenharmony_ci}; 588c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, karma_usb_ids); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci#undef UNUSUAL_DEV 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci/* 638c2ecf20Sopenharmony_ci * The flags table 648c2ecf20Sopenharmony_ci */ 658c2ecf20Sopenharmony_ci#define UNUSUAL_DEV(idVendor, idProduct, bcdDeviceMin, bcdDeviceMax, \ 668c2ecf20Sopenharmony_ci vendor_name, product_name, use_protocol, use_transport, \ 678c2ecf20Sopenharmony_ci init_function, Flags) \ 688c2ecf20Sopenharmony_ci{ \ 698c2ecf20Sopenharmony_ci .vendorName = vendor_name, \ 708c2ecf20Sopenharmony_ci .productName = product_name, \ 718c2ecf20Sopenharmony_ci .useProtocol = use_protocol, \ 728c2ecf20Sopenharmony_ci .useTransport = use_transport, \ 738c2ecf20Sopenharmony_ci .initFunction = init_function, \ 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic struct us_unusual_dev karma_unusual_dev_list[] = { 778c2ecf20Sopenharmony_ci# include "unusual_karma.h" 788c2ecf20Sopenharmony_ci { } /* Terminating entry */ 798c2ecf20Sopenharmony_ci}; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci#undef UNUSUAL_DEV 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci/* 858c2ecf20Sopenharmony_ci * Send commands to Rio Karma. 868c2ecf20Sopenharmony_ci * 878c2ecf20Sopenharmony_ci * For each command we send 40 bytes starting 'RIOP\0' followed by 888c2ecf20Sopenharmony_ci * the command number and a sequence number, which the device will ack 898c2ecf20Sopenharmony_ci * with a 512-byte packet with the high four bits set and everything 908c2ecf20Sopenharmony_ci * else null. Then we send 'RIOP\x80' followed by a zero and the 918c2ecf20Sopenharmony_ci * sequence number, until byte 5 in the response repeats the sequence 928c2ecf20Sopenharmony_ci * number. 938c2ecf20Sopenharmony_ci */ 948c2ecf20Sopenharmony_cistatic int rio_karma_send_command(char cmd, struct us_data *us) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci int result; 978c2ecf20Sopenharmony_ci unsigned long timeout; 988c2ecf20Sopenharmony_ci static unsigned char seq = 1; 998c2ecf20Sopenharmony_ci struct karma_data *data = (struct karma_data *) us->extra; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci usb_stor_dbg(us, "sending command %04x\n", cmd); 1028c2ecf20Sopenharmony_ci memset(us->iobuf, 0, RIO_SEND_LEN); 1038c2ecf20Sopenharmony_ci memcpy(us->iobuf, RIO_PREFIX, RIO_PREFIX_LEN); 1048c2ecf20Sopenharmony_ci us->iobuf[5] = cmd; 1058c2ecf20Sopenharmony_ci us->iobuf[6] = seq; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(6000); 1088c2ecf20Sopenharmony_ci for (;;) { 1098c2ecf20Sopenharmony_ci result = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, 1108c2ecf20Sopenharmony_ci us->iobuf, RIO_SEND_LEN, NULL); 1118c2ecf20Sopenharmony_ci if (result != USB_STOR_XFER_GOOD) 1128c2ecf20Sopenharmony_ci goto err; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, 1158c2ecf20Sopenharmony_ci data->recv, RIO_RECV_LEN, NULL); 1168c2ecf20Sopenharmony_ci if (result != USB_STOR_XFER_GOOD) 1178c2ecf20Sopenharmony_ci goto err; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (data->recv[5] == seq) 1208c2ecf20Sopenharmony_ci break; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci if (time_after(jiffies, timeout)) 1238c2ecf20Sopenharmony_ci goto err; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci us->iobuf[4] = 0x80; 1268c2ecf20Sopenharmony_ci us->iobuf[5] = 0; 1278c2ecf20Sopenharmony_ci msleep(50); 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci seq++; 1318c2ecf20Sopenharmony_ci if (seq == 0) 1328c2ecf20Sopenharmony_ci seq = 1; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci usb_stor_dbg(us, "sent command %04x\n", cmd); 1358c2ecf20Sopenharmony_ci return 0; 1368c2ecf20Sopenharmony_cierr: 1378c2ecf20Sopenharmony_ci usb_stor_dbg(us, "command %04x failed\n", cmd); 1388c2ecf20Sopenharmony_ci return USB_STOR_TRANSPORT_FAILED; 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci/* 1428c2ecf20Sopenharmony_ci * Trap START_STOP and READ_10 to leave/re-enter storage mode. 1438c2ecf20Sopenharmony_ci * Everything else is propagated to the normal bulk layer. 1448c2ecf20Sopenharmony_ci */ 1458c2ecf20Sopenharmony_cistatic int rio_karma_transport(struct scsi_cmnd *srb, struct us_data *us) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci int ret; 1488c2ecf20Sopenharmony_ci struct karma_data *data = (struct karma_data *) us->extra; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci if (srb->cmnd[0] == READ_10 && !data->in_storage) { 1518c2ecf20Sopenharmony_ci ret = rio_karma_send_command(RIO_ENTER_STORAGE, us); 1528c2ecf20Sopenharmony_ci if (ret) 1538c2ecf20Sopenharmony_ci return ret; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci data->in_storage = 1; 1568c2ecf20Sopenharmony_ci return usb_stor_Bulk_transport(srb, us); 1578c2ecf20Sopenharmony_ci } else if (srb->cmnd[0] == START_STOP) { 1588c2ecf20Sopenharmony_ci ret = rio_karma_send_command(RIO_LEAVE_STORAGE, us); 1598c2ecf20Sopenharmony_ci if (ret) 1608c2ecf20Sopenharmony_ci return ret; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci data->in_storage = 0; 1638c2ecf20Sopenharmony_ci return rio_karma_send_command(RIO_RESET, us); 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci return usb_stor_Bulk_transport(srb, us); 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic void rio_karma_destructor(void *extra) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci struct karma_data *data = (struct karma_data *) extra; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci kfree(data->recv); 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic int rio_karma_init(struct us_data *us) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci struct karma_data *data = kzalloc(sizeof(struct karma_data), GFP_NOIO); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci if (!data) 1808c2ecf20Sopenharmony_ci return -ENOMEM; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci data->recv = kmalloc(RIO_RECV_LEN, GFP_NOIO); 1838c2ecf20Sopenharmony_ci if (!data->recv) { 1848c2ecf20Sopenharmony_ci kfree(data); 1858c2ecf20Sopenharmony_ci return -ENOMEM; 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci us->extra = data; 1898c2ecf20Sopenharmony_ci us->extra_destructor = rio_karma_destructor; 1908c2ecf20Sopenharmony_ci if (rio_karma_send_command(RIO_ENTER_STORAGE, us)) 1918c2ecf20Sopenharmony_ci return -EIO; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci data->in_storage = 1; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci return 0; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic struct scsi_host_template karma_host_template; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic int karma_probe(struct usb_interface *intf, 2018c2ecf20Sopenharmony_ci const struct usb_device_id *id) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci struct us_data *us; 2048c2ecf20Sopenharmony_ci int result; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci result = usb_stor_probe1(&us, intf, id, 2078c2ecf20Sopenharmony_ci (id - karma_usb_ids) + karma_unusual_dev_list, 2088c2ecf20Sopenharmony_ci &karma_host_template); 2098c2ecf20Sopenharmony_ci if (result) 2108c2ecf20Sopenharmony_ci return result; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci us->transport_name = "Rio Karma/Bulk"; 2138c2ecf20Sopenharmony_ci us->transport = rio_karma_transport; 2148c2ecf20Sopenharmony_ci us->transport_reset = usb_stor_Bulk_reset; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci result = usb_stor_probe2(us); 2178c2ecf20Sopenharmony_ci return result; 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic struct usb_driver karma_driver = { 2218c2ecf20Sopenharmony_ci .name = DRV_NAME, 2228c2ecf20Sopenharmony_ci .probe = karma_probe, 2238c2ecf20Sopenharmony_ci .disconnect = usb_stor_disconnect, 2248c2ecf20Sopenharmony_ci .suspend = usb_stor_suspend, 2258c2ecf20Sopenharmony_ci .resume = usb_stor_resume, 2268c2ecf20Sopenharmony_ci .reset_resume = usb_stor_reset_resume, 2278c2ecf20Sopenharmony_ci .pre_reset = usb_stor_pre_reset, 2288c2ecf20Sopenharmony_ci .post_reset = usb_stor_post_reset, 2298c2ecf20Sopenharmony_ci .id_table = karma_usb_ids, 2308c2ecf20Sopenharmony_ci .soft_unbind = 1, 2318c2ecf20Sopenharmony_ci .no_dynamic_id = 1, 2328c2ecf20Sopenharmony_ci}; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cimodule_usb_stor_driver(karma_driver, karma_host_template, DRV_NAME); 235