18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Driver for SanDisk SDDR-55 SmartMedia reader
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * SDDR55 driver v0.1:
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * First release
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Current development and maintenance by:
108c2ecf20Sopenharmony_ci *   (c) 2002 Simon Munton
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/jiffies.h>
148c2ecf20Sopenharmony_ci#include <linux/errno.h>
158c2ecf20Sopenharmony_ci#include <linux/module.h>
168c2ecf20Sopenharmony_ci#include <linux/slab.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include <scsi/scsi.h>
198c2ecf20Sopenharmony_ci#include <scsi/scsi_cmnd.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include "usb.h"
228c2ecf20Sopenharmony_ci#include "transport.h"
238c2ecf20Sopenharmony_ci#include "protocol.h"
248c2ecf20Sopenharmony_ci#include "debug.h"
258c2ecf20Sopenharmony_ci#include "scsiglue.h"
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#define DRV_NAME "ums-sddr55"
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Driver for SanDisk SDDR-55 SmartMedia reader");
308c2ecf20Sopenharmony_ciMODULE_AUTHOR("Simon Munton");
318c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
328c2ecf20Sopenharmony_ciMODULE_IMPORT_NS(USB_STORAGE);
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci/*
358c2ecf20Sopenharmony_ci * The table of devices
368c2ecf20Sopenharmony_ci */
378c2ecf20Sopenharmony_ci#define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \
388c2ecf20Sopenharmony_ci		    vendorName, productName, useProtocol, useTransport, \
398c2ecf20Sopenharmony_ci		    initFunction, flags) \
408c2ecf20Sopenharmony_ci{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
418c2ecf20Sopenharmony_ci  .driver_info = (flags) }
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistatic struct usb_device_id sddr55_usb_ids[] = {
448c2ecf20Sopenharmony_ci#	include "unusual_sddr55.h"
458c2ecf20Sopenharmony_ci	{ }		/* Terminating entry */
468c2ecf20Sopenharmony_ci};
478c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, sddr55_usb_ids);
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci#undef UNUSUAL_DEV
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci/*
528c2ecf20Sopenharmony_ci * The flags table
538c2ecf20Sopenharmony_ci */
548c2ecf20Sopenharmony_ci#define UNUSUAL_DEV(idVendor, idProduct, bcdDeviceMin, bcdDeviceMax, \
558c2ecf20Sopenharmony_ci		    vendor_name, product_name, use_protocol, use_transport, \
568c2ecf20Sopenharmony_ci		    init_function, Flags) \
578c2ecf20Sopenharmony_ci{ \
588c2ecf20Sopenharmony_ci	.vendorName = vendor_name,	\
598c2ecf20Sopenharmony_ci	.productName = product_name,	\
608c2ecf20Sopenharmony_ci	.useProtocol = use_protocol,	\
618c2ecf20Sopenharmony_ci	.useTransport = use_transport,	\
628c2ecf20Sopenharmony_ci	.initFunction = init_function,	\
638c2ecf20Sopenharmony_ci}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic struct us_unusual_dev sddr55_unusual_dev_list[] = {
668c2ecf20Sopenharmony_ci#	include "unusual_sddr55.h"
678c2ecf20Sopenharmony_ci	{ }		/* Terminating entry */
688c2ecf20Sopenharmony_ci};
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci#undef UNUSUAL_DEV
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci#define short_pack(lsb,msb) ( ((u16)(lsb)) | ( ((u16)(msb))<<8 ) )
748c2ecf20Sopenharmony_ci#define LSB_of(s) ((s)&0xFF)
758c2ecf20Sopenharmony_ci#define MSB_of(s) ((s)>>8)
768c2ecf20Sopenharmony_ci#define PAGESIZE  512
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci#define set_sense_info(sk, asc, ascq)	\
798c2ecf20Sopenharmony_ci    do {				\
808c2ecf20Sopenharmony_ci	info->sense_data[2] = sk;	\
818c2ecf20Sopenharmony_ci	info->sense_data[12] = asc;	\
828c2ecf20Sopenharmony_ci	info->sense_data[13] = ascq;	\
838c2ecf20Sopenharmony_ci	} while (0)
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistruct sddr55_card_info {
878c2ecf20Sopenharmony_ci	unsigned long	capacity;	/* Size of card in bytes */
888c2ecf20Sopenharmony_ci	int		max_log_blks;	/* maximum number of logical blocks */
898c2ecf20Sopenharmony_ci	int		pageshift;	/* log2 of pagesize */
908c2ecf20Sopenharmony_ci	int		smallpageshift;	/* 1 if pagesize == 256 */
918c2ecf20Sopenharmony_ci	int		blocksize;	/* Size of block in pages */
928c2ecf20Sopenharmony_ci	int		blockshift;	/* log2 of blocksize */
938c2ecf20Sopenharmony_ci	int		blockmask;	/* 2^blockshift - 1 */
948c2ecf20Sopenharmony_ci	int		read_only;	/* non zero if card is write protected */
958c2ecf20Sopenharmony_ci	int		force_read_only;	/* non zero if we find a map error*/
968c2ecf20Sopenharmony_ci	int		*lba_to_pba;	/* logical to physical map */
978c2ecf20Sopenharmony_ci	int		*pba_to_lba;	/* physical to logical map */
988c2ecf20Sopenharmony_ci	int		fatal_error;	/* set if we detect something nasty */
998c2ecf20Sopenharmony_ci	unsigned long 	last_access;	/* number of jiffies since we last talked to device */
1008c2ecf20Sopenharmony_ci	unsigned char   sense_data[18];
1018c2ecf20Sopenharmony_ci};
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci#define NOT_ALLOCATED		0xffffffff
1058c2ecf20Sopenharmony_ci#define BAD_BLOCK		0xffff
1068c2ecf20Sopenharmony_ci#define CIS_BLOCK		0x400
1078c2ecf20Sopenharmony_ci#define UNUSED_BLOCK		0x3ff
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistatic int
1108c2ecf20Sopenharmony_cisddr55_bulk_transport(struct us_data *us, int direction,
1118c2ecf20Sopenharmony_ci		      unsigned char *data, unsigned int len) {
1128c2ecf20Sopenharmony_ci	struct sddr55_card_info *info = (struct sddr55_card_info *)us->extra;
1138c2ecf20Sopenharmony_ci	unsigned int pipe = (direction == DMA_FROM_DEVICE) ?
1148c2ecf20Sopenharmony_ci			us->recv_bulk_pipe : us->send_bulk_pipe;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	if (!len)
1178c2ecf20Sopenharmony_ci		return USB_STOR_XFER_GOOD;
1188c2ecf20Sopenharmony_ci	info->last_access = jiffies;
1198c2ecf20Sopenharmony_ci	return usb_stor_bulk_transfer_buf(us, pipe, data, len, NULL);
1208c2ecf20Sopenharmony_ci}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci/*
1238c2ecf20Sopenharmony_ci * check if card inserted, if there is, update read_only status
1248c2ecf20Sopenharmony_ci * return non zero if no card
1258c2ecf20Sopenharmony_ci */
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_cistatic int sddr55_status(struct us_data *us)
1288c2ecf20Sopenharmony_ci{
1298c2ecf20Sopenharmony_ci	int result;
1308c2ecf20Sopenharmony_ci	unsigned char *command = us->iobuf;
1318c2ecf20Sopenharmony_ci	unsigned char *status = us->iobuf;
1328c2ecf20Sopenharmony_ci	struct sddr55_card_info *info = (struct sddr55_card_info *)us->extra;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	/* send command */
1358c2ecf20Sopenharmony_ci	memset(command, 0, 8);
1368c2ecf20Sopenharmony_ci	command[5] = 0xB0;
1378c2ecf20Sopenharmony_ci	command[7] = 0x80;
1388c2ecf20Sopenharmony_ci	result = sddr55_bulk_transport(us,
1398c2ecf20Sopenharmony_ci		DMA_TO_DEVICE, command, 8);
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	usb_stor_dbg(us, "Result for send_command in status %d\n", result);
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	if (result != USB_STOR_XFER_GOOD) {
1448c2ecf20Sopenharmony_ci		set_sense_info (4, 0, 0);	/* hardware error */
1458c2ecf20Sopenharmony_ci		return USB_STOR_TRANSPORT_ERROR;
1468c2ecf20Sopenharmony_ci	}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	result = sddr55_bulk_transport(us,
1498c2ecf20Sopenharmony_ci		DMA_FROM_DEVICE, status,	4);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	/* expect to get short transfer if no card fitted */
1528c2ecf20Sopenharmony_ci	if (result == USB_STOR_XFER_SHORT || result == USB_STOR_XFER_STALLED) {
1538c2ecf20Sopenharmony_ci		/* had a short transfer, no card inserted, free map memory */
1548c2ecf20Sopenharmony_ci		kfree(info->lba_to_pba);
1558c2ecf20Sopenharmony_ci		kfree(info->pba_to_lba);
1568c2ecf20Sopenharmony_ci		info->lba_to_pba = NULL;
1578c2ecf20Sopenharmony_ci		info->pba_to_lba = NULL;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci		info->fatal_error = 0;
1608c2ecf20Sopenharmony_ci		info->force_read_only = 0;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci		set_sense_info (2, 0x3a, 0);	/* not ready, medium not present */
1638c2ecf20Sopenharmony_ci		return USB_STOR_TRANSPORT_FAILED;
1648c2ecf20Sopenharmony_ci	}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	if (result != USB_STOR_XFER_GOOD) {
1678c2ecf20Sopenharmony_ci		set_sense_info (4, 0, 0);	/* hardware error */
1688c2ecf20Sopenharmony_ci		return USB_STOR_TRANSPORT_FAILED;
1698c2ecf20Sopenharmony_ci	}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	/* check write protect status */
1728c2ecf20Sopenharmony_ci	info->read_only = (status[0] & 0x20);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	/* now read status */
1758c2ecf20Sopenharmony_ci	result = sddr55_bulk_transport(us,
1768c2ecf20Sopenharmony_ci		DMA_FROM_DEVICE, status,	2);
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	if (result != USB_STOR_XFER_GOOD) {
1798c2ecf20Sopenharmony_ci		set_sense_info (4, 0, 0);	/* hardware error */
1808c2ecf20Sopenharmony_ci	}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	return (result == USB_STOR_XFER_GOOD ?
1838c2ecf20Sopenharmony_ci			USB_STOR_TRANSPORT_GOOD : USB_STOR_TRANSPORT_FAILED);
1848c2ecf20Sopenharmony_ci}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_cistatic int sddr55_read_data(struct us_data *us,
1888c2ecf20Sopenharmony_ci		unsigned int lba,
1898c2ecf20Sopenharmony_ci		unsigned int page,
1908c2ecf20Sopenharmony_ci		unsigned short sectors) {
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	int result = USB_STOR_TRANSPORT_GOOD;
1938c2ecf20Sopenharmony_ci	unsigned char *command = us->iobuf;
1948c2ecf20Sopenharmony_ci	unsigned char *status = us->iobuf;
1958c2ecf20Sopenharmony_ci	struct sddr55_card_info *info = (struct sddr55_card_info *)us->extra;
1968c2ecf20Sopenharmony_ci	unsigned char *buffer;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	unsigned int pba;
1998c2ecf20Sopenharmony_ci	unsigned long address;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	unsigned short pages;
2028c2ecf20Sopenharmony_ci	unsigned int len, offset;
2038c2ecf20Sopenharmony_ci	struct scatterlist *sg;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	// Since we only read in one block at a time, we have to create
2068c2ecf20Sopenharmony_ci	// a bounce buffer and move the data a piece at a time between the
2078c2ecf20Sopenharmony_ci	// bounce buffer and the actual transfer buffer.
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	len = min((unsigned int) sectors, (unsigned int) info->blocksize >>
2108c2ecf20Sopenharmony_ci			info->smallpageshift) * PAGESIZE;
2118c2ecf20Sopenharmony_ci	buffer = kmalloc(len, GFP_NOIO);
2128c2ecf20Sopenharmony_ci	if (buffer == NULL)
2138c2ecf20Sopenharmony_ci		return USB_STOR_TRANSPORT_ERROR; /* out of memory */
2148c2ecf20Sopenharmony_ci	offset = 0;
2158c2ecf20Sopenharmony_ci	sg = NULL;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	while (sectors>0) {
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci		/* have we got to end? */
2208c2ecf20Sopenharmony_ci		if (lba >= info->max_log_blks)
2218c2ecf20Sopenharmony_ci			break;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci		pba = info->lba_to_pba[lba];
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci		// Read as many sectors as possible in this block
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci		pages = min((unsigned int) sectors << info->smallpageshift,
2288c2ecf20Sopenharmony_ci				info->blocksize - page);
2298c2ecf20Sopenharmony_ci		len = pages << info->pageshift;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci		usb_stor_dbg(us, "Read %02X pages, from PBA %04X (LBA %04X) page %02X\n",
2328c2ecf20Sopenharmony_ci			     pages, pba, lba, page);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci		if (pba == NOT_ALLOCATED) {
2358c2ecf20Sopenharmony_ci			/* no pba for this lba, fill with zeroes */
2368c2ecf20Sopenharmony_ci			memset (buffer, 0, len);
2378c2ecf20Sopenharmony_ci		} else {
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci			address = (pba << info->blockshift) + page;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci			command[0] = 0;
2428c2ecf20Sopenharmony_ci			command[1] = LSB_of(address>>16);
2438c2ecf20Sopenharmony_ci			command[2] = LSB_of(address>>8);
2448c2ecf20Sopenharmony_ci			command[3] = LSB_of(address);
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci			command[4] = 0;
2478c2ecf20Sopenharmony_ci			command[5] = 0xB0;
2488c2ecf20Sopenharmony_ci			command[6] = LSB_of(pages << (1 - info->smallpageshift));
2498c2ecf20Sopenharmony_ci			command[7] = 0x85;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci			/* send command */
2528c2ecf20Sopenharmony_ci			result = sddr55_bulk_transport(us,
2538c2ecf20Sopenharmony_ci				DMA_TO_DEVICE, command, 8);
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci			usb_stor_dbg(us, "Result for send_command in read_data %d\n",
2568c2ecf20Sopenharmony_ci				     result);
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci			if (result != USB_STOR_XFER_GOOD) {
2598c2ecf20Sopenharmony_ci				result = USB_STOR_TRANSPORT_ERROR;
2608c2ecf20Sopenharmony_ci				goto leave;
2618c2ecf20Sopenharmony_ci			}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci			/* read data */
2648c2ecf20Sopenharmony_ci			result = sddr55_bulk_transport(us,
2658c2ecf20Sopenharmony_ci				DMA_FROM_DEVICE, buffer, len);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci			if (result != USB_STOR_XFER_GOOD) {
2688c2ecf20Sopenharmony_ci				result = USB_STOR_TRANSPORT_ERROR;
2698c2ecf20Sopenharmony_ci				goto leave;
2708c2ecf20Sopenharmony_ci			}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci			/* now read status */
2738c2ecf20Sopenharmony_ci			result = sddr55_bulk_transport(us,
2748c2ecf20Sopenharmony_ci				DMA_FROM_DEVICE, status, 2);
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci			if (result != USB_STOR_XFER_GOOD) {
2778c2ecf20Sopenharmony_ci				result = USB_STOR_TRANSPORT_ERROR;
2788c2ecf20Sopenharmony_ci				goto leave;
2798c2ecf20Sopenharmony_ci			}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci			/* check status for error */
2828c2ecf20Sopenharmony_ci			if (status[0] == 0xff && status[1] == 0x4) {
2838c2ecf20Sopenharmony_ci				set_sense_info (3, 0x11, 0);
2848c2ecf20Sopenharmony_ci				result = USB_STOR_TRANSPORT_FAILED;
2858c2ecf20Sopenharmony_ci				goto leave;
2868c2ecf20Sopenharmony_ci			}
2878c2ecf20Sopenharmony_ci		}
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci		// Store the data in the transfer buffer
2908c2ecf20Sopenharmony_ci		usb_stor_access_xfer_buf(buffer, len, us->srb,
2918c2ecf20Sopenharmony_ci				&sg, &offset, TO_XFER_BUF);
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci		page = 0;
2948c2ecf20Sopenharmony_ci		lba++;
2958c2ecf20Sopenharmony_ci		sectors -= pages >> info->smallpageshift;
2968c2ecf20Sopenharmony_ci	}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	result = USB_STOR_TRANSPORT_GOOD;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_cileave:
3018c2ecf20Sopenharmony_ci	kfree(buffer);
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	return result;
3048c2ecf20Sopenharmony_ci}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_cistatic int sddr55_write_data(struct us_data *us,
3078c2ecf20Sopenharmony_ci		unsigned int lba,
3088c2ecf20Sopenharmony_ci		unsigned int page,
3098c2ecf20Sopenharmony_ci		unsigned short sectors) {
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	int result = USB_STOR_TRANSPORT_GOOD;
3128c2ecf20Sopenharmony_ci	unsigned char *command = us->iobuf;
3138c2ecf20Sopenharmony_ci	unsigned char *status = us->iobuf;
3148c2ecf20Sopenharmony_ci	struct sddr55_card_info *info = (struct sddr55_card_info *)us->extra;
3158c2ecf20Sopenharmony_ci	unsigned char *buffer;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	unsigned int pba;
3188c2ecf20Sopenharmony_ci	unsigned int new_pba;
3198c2ecf20Sopenharmony_ci	unsigned long address;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	unsigned short pages;
3228c2ecf20Sopenharmony_ci	int i;
3238c2ecf20Sopenharmony_ci	unsigned int len, offset;
3248c2ecf20Sopenharmony_ci	struct scatterlist *sg;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	/* check if we are allowed to write */
3278c2ecf20Sopenharmony_ci	if (info->read_only || info->force_read_only) {
3288c2ecf20Sopenharmony_ci		set_sense_info (7, 0x27, 0);	/* read only */
3298c2ecf20Sopenharmony_ci		return USB_STOR_TRANSPORT_FAILED;
3308c2ecf20Sopenharmony_ci	}
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	// Since we only write one block at a time, we have to create
3338c2ecf20Sopenharmony_ci	// a bounce buffer and move the data a piece at a time between the
3348c2ecf20Sopenharmony_ci	// bounce buffer and the actual transfer buffer.
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	len = min((unsigned int) sectors, (unsigned int) info->blocksize >>
3378c2ecf20Sopenharmony_ci			info->smallpageshift) * PAGESIZE;
3388c2ecf20Sopenharmony_ci	buffer = kmalloc(len, GFP_NOIO);
3398c2ecf20Sopenharmony_ci	if (buffer == NULL)
3408c2ecf20Sopenharmony_ci		return USB_STOR_TRANSPORT_ERROR;
3418c2ecf20Sopenharmony_ci	offset = 0;
3428c2ecf20Sopenharmony_ci	sg = NULL;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	while (sectors > 0) {
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci		/* have we got to end? */
3478c2ecf20Sopenharmony_ci		if (lba >= info->max_log_blks)
3488c2ecf20Sopenharmony_ci			break;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci		pba = info->lba_to_pba[lba];
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci		// Write as many sectors as possible in this block
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci		pages = min((unsigned int) sectors << info->smallpageshift,
3558c2ecf20Sopenharmony_ci				info->blocksize - page);
3568c2ecf20Sopenharmony_ci		len = pages << info->pageshift;
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci		// Get the data from the transfer buffer
3598c2ecf20Sopenharmony_ci		usb_stor_access_xfer_buf(buffer, len, us->srb,
3608c2ecf20Sopenharmony_ci				&sg, &offset, FROM_XFER_BUF);
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci		usb_stor_dbg(us, "Write %02X pages, to PBA %04X (LBA %04X) page %02X\n",
3638c2ecf20Sopenharmony_ci			     pages, pba, lba, page);
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci		command[4] = 0;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci		if (pba == NOT_ALLOCATED) {
3688c2ecf20Sopenharmony_ci			/* no pba allocated for this lba, find a free pba to use */
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci			int max_pba = (info->max_log_blks / 250 ) * 256;
3718c2ecf20Sopenharmony_ci			int found_count = 0;
3728c2ecf20Sopenharmony_ci			int found_pba = -1;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci			/* set pba to first block in zone lba is in */
3758c2ecf20Sopenharmony_ci			pba = (lba / 1000) * 1024;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci			usb_stor_dbg(us, "No PBA for LBA %04X\n", lba);
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci			if (max_pba > 1024)
3808c2ecf20Sopenharmony_ci				max_pba = 1024;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci			/*
3838c2ecf20Sopenharmony_ci			 * Scan through the map looking for an unused block
3848c2ecf20Sopenharmony_ci			 * leave 16 unused blocks at start (or as many as
3858c2ecf20Sopenharmony_ci			 * possible) since the sddr55 seems to reuse a used
3868c2ecf20Sopenharmony_ci			 * block when it shouldn't if we don't leave space.
3878c2ecf20Sopenharmony_ci			 */
3888c2ecf20Sopenharmony_ci			for (i = 0; i < max_pba; i++, pba++) {
3898c2ecf20Sopenharmony_ci				if (info->pba_to_lba[pba] == UNUSED_BLOCK) {
3908c2ecf20Sopenharmony_ci					found_pba = pba;
3918c2ecf20Sopenharmony_ci					if (found_count++ > 16)
3928c2ecf20Sopenharmony_ci						break;
3938c2ecf20Sopenharmony_ci				}
3948c2ecf20Sopenharmony_ci			}
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci			pba = found_pba;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci			if (pba == -1) {
3998c2ecf20Sopenharmony_ci				/* oh dear */
4008c2ecf20Sopenharmony_ci				usb_stor_dbg(us, "Couldn't find unallocated block\n");
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci				set_sense_info (3, 0x31, 0);	/* medium error */
4038c2ecf20Sopenharmony_ci				result = USB_STOR_TRANSPORT_FAILED;
4048c2ecf20Sopenharmony_ci				goto leave;
4058c2ecf20Sopenharmony_ci			}
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci			usb_stor_dbg(us, "Allocating PBA %04X for LBA %04X\n",
4088c2ecf20Sopenharmony_ci				     pba, lba);
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci			/* set writing to unallocated block flag */
4118c2ecf20Sopenharmony_ci			command[4] = 0x40;
4128c2ecf20Sopenharmony_ci		}
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci		address = (pba << info->blockshift) + page;
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci		command[1] = LSB_of(address>>16);
4178c2ecf20Sopenharmony_ci		command[2] = LSB_of(address>>8);
4188c2ecf20Sopenharmony_ci		command[3] = LSB_of(address);
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci		/* set the lba into the command, modulo 1000 */
4218c2ecf20Sopenharmony_ci		command[0] = LSB_of(lba % 1000);
4228c2ecf20Sopenharmony_ci		command[6] = MSB_of(lba % 1000);
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci		command[4] |= LSB_of(pages >> info->smallpageshift);
4258c2ecf20Sopenharmony_ci		command[5] = 0xB0;
4268c2ecf20Sopenharmony_ci		command[7] = 0x86;
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci		/* send command */
4298c2ecf20Sopenharmony_ci		result = sddr55_bulk_transport(us,
4308c2ecf20Sopenharmony_ci			DMA_TO_DEVICE, command, 8);
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci		if (result != USB_STOR_XFER_GOOD) {
4338c2ecf20Sopenharmony_ci			usb_stor_dbg(us, "Result for send_command in write_data %d\n",
4348c2ecf20Sopenharmony_ci				     result);
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci			/* set_sense_info is superfluous here? */
4378c2ecf20Sopenharmony_ci			set_sense_info (3, 0x3, 0);/* peripheral write error */
4388c2ecf20Sopenharmony_ci			result = USB_STOR_TRANSPORT_FAILED;
4398c2ecf20Sopenharmony_ci			goto leave;
4408c2ecf20Sopenharmony_ci		}
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci		/* send the data */
4438c2ecf20Sopenharmony_ci		result = sddr55_bulk_transport(us,
4448c2ecf20Sopenharmony_ci			DMA_TO_DEVICE, buffer, len);
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci		if (result != USB_STOR_XFER_GOOD) {
4478c2ecf20Sopenharmony_ci			usb_stor_dbg(us, "Result for send_data in write_data %d\n",
4488c2ecf20Sopenharmony_ci				     result);
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci			/* set_sense_info is superfluous here? */
4518c2ecf20Sopenharmony_ci			set_sense_info (3, 0x3, 0);/* peripheral write error */
4528c2ecf20Sopenharmony_ci			result = USB_STOR_TRANSPORT_FAILED;
4538c2ecf20Sopenharmony_ci			goto leave;
4548c2ecf20Sopenharmony_ci		}
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci		/* now read status */
4578c2ecf20Sopenharmony_ci		result = sddr55_bulk_transport(us, DMA_FROM_DEVICE, status, 6);
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci		if (result != USB_STOR_XFER_GOOD) {
4608c2ecf20Sopenharmony_ci			usb_stor_dbg(us, "Result for get_status in write_data %d\n",
4618c2ecf20Sopenharmony_ci				     result);
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci			/* set_sense_info is superfluous here? */
4648c2ecf20Sopenharmony_ci			set_sense_info (3, 0x3, 0);/* peripheral write error */
4658c2ecf20Sopenharmony_ci			result = USB_STOR_TRANSPORT_FAILED;
4668c2ecf20Sopenharmony_ci			goto leave;
4678c2ecf20Sopenharmony_ci		}
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci		new_pba = (status[3] + (status[4] << 8) + (status[5] << 16))
4708c2ecf20Sopenharmony_ci						  >> info->blockshift;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci		/* check status for error */
4738c2ecf20Sopenharmony_ci		if (status[0] == 0xff && status[1] == 0x4) {
4748c2ecf20Sopenharmony_ci			info->pba_to_lba[new_pba] = BAD_BLOCK;
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci			set_sense_info (3, 0x0c, 0);
4778c2ecf20Sopenharmony_ci			result = USB_STOR_TRANSPORT_FAILED;
4788c2ecf20Sopenharmony_ci			goto leave;
4798c2ecf20Sopenharmony_ci		}
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci		usb_stor_dbg(us, "Updating maps for LBA %04X: old PBA %04X, new PBA %04X\n",
4828c2ecf20Sopenharmony_ci			     lba, pba, new_pba);
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci		/* update the lba<->pba maps, note new_pba might be the same as pba */
4858c2ecf20Sopenharmony_ci		info->lba_to_pba[lba] = new_pba;
4868c2ecf20Sopenharmony_ci		info->pba_to_lba[pba] = UNUSED_BLOCK;
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci		/* check that new_pba wasn't already being used */
4898c2ecf20Sopenharmony_ci		if (info->pba_to_lba[new_pba] != UNUSED_BLOCK) {
4908c2ecf20Sopenharmony_ci			printk(KERN_ERR "sddr55 error: new PBA %04X already in use for LBA %04X\n",
4918c2ecf20Sopenharmony_ci				new_pba, info->pba_to_lba[new_pba]);
4928c2ecf20Sopenharmony_ci			info->fatal_error = 1;
4938c2ecf20Sopenharmony_ci			set_sense_info (3, 0x31, 0);
4948c2ecf20Sopenharmony_ci			result = USB_STOR_TRANSPORT_FAILED;
4958c2ecf20Sopenharmony_ci			goto leave;
4968c2ecf20Sopenharmony_ci		}
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci		/* update the pba<->lba maps for new_pba */
4998c2ecf20Sopenharmony_ci		info->pba_to_lba[new_pba] = lba % 1000;
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci		page = 0;
5028c2ecf20Sopenharmony_ci		lba++;
5038c2ecf20Sopenharmony_ci		sectors -= pages >> info->smallpageshift;
5048c2ecf20Sopenharmony_ci	}
5058c2ecf20Sopenharmony_ci	result = USB_STOR_TRANSPORT_GOOD;
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci leave:
5088c2ecf20Sopenharmony_ci	kfree(buffer);
5098c2ecf20Sopenharmony_ci	return result;
5108c2ecf20Sopenharmony_ci}
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_cistatic int sddr55_read_deviceID(struct us_data *us,
5138c2ecf20Sopenharmony_ci		unsigned char *manufacturerID,
5148c2ecf20Sopenharmony_ci		unsigned char *deviceID) {
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	int result;
5178c2ecf20Sopenharmony_ci	unsigned char *command = us->iobuf;
5188c2ecf20Sopenharmony_ci	unsigned char *content = us->iobuf;
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	memset(command, 0, 8);
5218c2ecf20Sopenharmony_ci	command[5] = 0xB0;
5228c2ecf20Sopenharmony_ci	command[7] = 0x84;
5238c2ecf20Sopenharmony_ci	result = sddr55_bulk_transport(us, DMA_TO_DEVICE, command, 8);
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	usb_stor_dbg(us, "Result of send_control for device ID is %d\n",
5268c2ecf20Sopenharmony_ci		     result);
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	if (result != USB_STOR_XFER_GOOD)
5298c2ecf20Sopenharmony_ci		return USB_STOR_TRANSPORT_ERROR;
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	result = sddr55_bulk_transport(us,
5328c2ecf20Sopenharmony_ci		DMA_FROM_DEVICE, content, 4);
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	if (result != USB_STOR_XFER_GOOD)
5358c2ecf20Sopenharmony_ci		return USB_STOR_TRANSPORT_ERROR;
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	*manufacturerID = content[0];
5388c2ecf20Sopenharmony_ci	*deviceID = content[1];
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	if (content[0] != 0xff)	{
5418c2ecf20Sopenharmony_ci    		result = sddr55_bulk_transport(us,
5428c2ecf20Sopenharmony_ci			DMA_FROM_DEVICE, content, 2);
5438c2ecf20Sopenharmony_ci	}
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	return USB_STOR_TRANSPORT_GOOD;
5468c2ecf20Sopenharmony_ci}
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_cistatic int sddr55_reset(struct us_data *us)
5508c2ecf20Sopenharmony_ci{
5518c2ecf20Sopenharmony_ci	return 0;
5528c2ecf20Sopenharmony_ci}
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_cistatic unsigned long sddr55_get_capacity(struct us_data *us) {
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	unsigned char manufacturerID;
5588c2ecf20Sopenharmony_ci	unsigned char deviceID;
5598c2ecf20Sopenharmony_ci	int result;
5608c2ecf20Sopenharmony_ci	struct sddr55_card_info *info = (struct sddr55_card_info *)us->extra;
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	usb_stor_dbg(us, "Reading capacity...\n");
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	result = sddr55_read_deviceID(us,
5658c2ecf20Sopenharmony_ci		&manufacturerID,
5668c2ecf20Sopenharmony_ci		&deviceID);
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	usb_stor_dbg(us, "Result of read_deviceID is %d\n", result);
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	if (result != USB_STOR_XFER_GOOD)
5718c2ecf20Sopenharmony_ci		return 0;
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	usb_stor_dbg(us, "Device ID = %02X\n", deviceID);
5748c2ecf20Sopenharmony_ci	usb_stor_dbg(us, "Manuf  ID = %02X\n", manufacturerID);
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	info->pageshift = 9;
5778c2ecf20Sopenharmony_ci	info->smallpageshift = 0;
5788c2ecf20Sopenharmony_ci	info->blocksize = 16;
5798c2ecf20Sopenharmony_ci	info->blockshift = 4;
5808c2ecf20Sopenharmony_ci	info->blockmask = 15;
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	switch (deviceID) {
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	case 0x6e: // 1MB
5858c2ecf20Sopenharmony_ci	case 0xe8:
5868c2ecf20Sopenharmony_ci	case 0xec:
5878c2ecf20Sopenharmony_ci		info->pageshift = 8;
5888c2ecf20Sopenharmony_ci		info->smallpageshift = 1;
5898c2ecf20Sopenharmony_ci		return 0x00100000;
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	case 0xea: // 2MB
5928c2ecf20Sopenharmony_ci	case 0x64:
5938c2ecf20Sopenharmony_ci		info->pageshift = 8;
5948c2ecf20Sopenharmony_ci		info->smallpageshift = 1;
5958c2ecf20Sopenharmony_ci		fallthrough;
5968c2ecf20Sopenharmony_ci	case 0x5d: // 5d is a ROM card with pagesize 512.
5978c2ecf20Sopenharmony_ci		return 0x00200000;
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	case 0xe3: // 4MB
6008c2ecf20Sopenharmony_ci	case 0xe5:
6018c2ecf20Sopenharmony_ci	case 0x6b:
6028c2ecf20Sopenharmony_ci	case 0xd5:
6038c2ecf20Sopenharmony_ci		return 0x00400000;
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	case 0xe6: // 8MB
6068c2ecf20Sopenharmony_ci	case 0xd6:
6078c2ecf20Sopenharmony_ci		return 0x00800000;
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	case 0x73: // 16MB
6108c2ecf20Sopenharmony_ci		info->blocksize = 32;
6118c2ecf20Sopenharmony_ci		info->blockshift = 5;
6128c2ecf20Sopenharmony_ci		info->blockmask = 31;
6138c2ecf20Sopenharmony_ci		return 0x01000000;
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	case 0x75: // 32MB
6168c2ecf20Sopenharmony_ci		info->blocksize = 32;
6178c2ecf20Sopenharmony_ci		info->blockshift = 5;
6188c2ecf20Sopenharmony_ci		info->blockmask = 31;
6198c2ecf20Sopenharmony_ci		return 0x02000000;
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	case 0x76: // 64MB
6228c2ecf20Sopenharmony_ci		info->blocksize = 32;
6238c2ecf20Sopenharmony_ci		info->blockshift = 5;
6248c2ecf20Sopenharmony_ci		info->blockmask = 31;
6258c2ecf20Sopenharmony_ci		return 0x04000000;
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	case 0x79: // 128MB
6288c2ecf20Sopenharmony_ci		info->blocksize = 32;
6298c2ecf20Sopenharmony_ci		info->blockshift = 5;
6308c2ecf20Sopenharmony_ci		info->blockmask = 31;
6318c2ecf20Sopenharmony_ci		return 0x08000000;
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	default: // unknown
6348c2ecf20Sopenharmony_ci		return 0;
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci	}
6378c2ecf20Sopenharmony_ci}
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_cistatic int sddr55_read_map(struct us_data *us) {
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	struct sddr55_card_info *info = (struct sddr55_card_info *)(us->extra);
6428c2ecf20Sopenharmony_ci	int numblocks;
6438c2ecf20Sopenharmony_ci	unsigned char *buffer;
6448c2ecf20Sopenharmony_ci	unsigned char *command = us->iobuf;
6458c2ecf20Sopenharmony_ci	int i;
6468c2ecf20Sopenharmony_ci	unsigned short lba;
6478c2ecf20Sopenharmony_ci	unsigned short max_lba;
6488c2ecf20Sopenharmony_ci	int result;
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	if (!info->capacity)
6518c2ecf20Sopenharmony_ci		return -1;
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	numblocks = info->capacity >> (info->blockshift + info->pageshift);
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci	buffer = kmalloc_array(numblocks, 2, GFP_NOIO );
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	if (!buffer)
6588c2ecf20Sopenharmony_ci		return -1;
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	memset(command, 0, 8);
6618c2ecf20Sopenharmony_ci	command[5] = 0xB0;
6628c2ecf20Sopenharmony_ci	command[6] = numblocks * 2 / 256;
6638c2ecf20Sopenharmony_ci	command[7] = 0x8A;
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	result = sddr55_bulk_transport(us, DMA_TO_DEVICE, command, 8);
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci	if ( result != USB_STOR_XFER_GOOD) {
6688c2ecf20Sopenharmony_ci		kfree (buffer);
6698c2ecf20Sopenharmony_ci		return -1;
6708c2ecf20Sopenharmony_ci	}
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	result = sddr55_bulk_transport(us, DMA_FROM_DEVICE, buffer, numblocks * 2);
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci	if ( result != USB_STOR_XFER_GOOD) {
6758c2ecf20Sopenharmony_ci		kfree (buffer);
6768c2ecf20Sopenharmony_ci		return -1;
6778c2ecf20Sopenharmony_ci	}
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci	result = sddr55_bulk_transport(us, DMA_FROM_DEVICE, command, 2);
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	if ( result != USB_STOR_XFER_GOOD) {
6828c2ecf20Sopenharmony_ci		kfree (buffer);
6838c2ecf20Sopenharmony_ci		return -1;
6848c2ecf20Sopenharmony_ci	}
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	kfree(info->lba_to_pba);
6878c2ecf20Sopenharmony_ci	kfree(info->pba_to_lba);
6888c2ecf20Sopenharmony_ci	info->lba_to_pba = kmalloc_array(numblocks, sizeof(int), GFP_NOIO);
6898c2ecf20Sopenharmony_ci	info->pba_to_lba = kmalloc_array(numblocks, sizeof(int), GFP_NOIO);
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	if (info->lba_to_pba == NULL || info->pba_to_lba == NULL) {
6928c2ecf20Sopenharmony_ci		kfree(info->lba_to_pba);
6938c2ecf20Sopenharmony_ci		kfree(info->pba_to_lba);
6948c2ecf20Sopenharmony_ci		info->lba_to_pba = NULL;
6958c2ecf20Sopenharmony_ci		info->pba_to_lba = NULL;
6968c2ecf20Sopenharmony_ci		kfree(buffer);
6978c2ecf20Sopenharmony_ci		return -1;
6988c2ecf20Sopenharmony_ci	}
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	memset(info->lba_to_pba, 0xff, numblocks*sizeof(int));
7018c2ecf20Sopenharmony_ci	memset(info->pba_to_lba, 0xff, numblocks*sizeof(int));
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci	/* set maximum lba */
7048c2ecf20Sopenharmony_ci	max_lba = info->max_log_blks;
7058c2ecf20Sopenharmony_ci	if (max_lba > 1000)
7068c2ecf20Sopenharmony_ci		max_lba = 1000;
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	/*
7098c2ecf20Sopenharmony_ci	 * Each block is 64 bytes of control data, so block i is located in
7108c2ecf20Sopenharmony_ci	 * scatterlist block i*64/128k = i*(2^6)*(2^-17) = i*(2^-11)
7118c2ecf20Sopenharmony_ci	 */
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	for (i=0; i<numblocks; i++) {
7148c2ecf20Sopenharmony_ci		int zone = i / 1024;
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci		lba = short_pack(buffer[i * 2], buffer[i * 2 + 1]);
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci			/*
7198c2ecf20Sopenharmony_ci			 * Every 1024 physical blocks ("zone"), the LBA numbers
7208c2ecf20Sopenharmony_ci			 * go back to zero, but are within a higher
7218c2ecf20Sopenharmony_ci			 * block of LBA's. Also, there is a maximum of
7228c2ecf20Sopenharmony_ci			 * 1000 LBA's per zone. In other words, in PBA
7238c2ecf20Sopenharmony_ci			 * 1024-2047 you will find LBA 0-999 which are
7248c2ecf20Sopenharmony_ci			 * really LBA 1000-1999. Yes, this wastes 24
7258c2ecf20Sopenharmony_ci			 * physical blocks per zone. Go figure.
7268c2ecf20Sopenharmony_ci			 * These devices can have blocks go bad, so there
7278c2ecf20Sopenharmony_ci			 * are 24 spare blocks to use when blocks do go bad.
7288c2ecf20Sopenharmony_ci			 */
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci			/*
7318c2ecf20Sopenharmony_ci			 * SDDR55 returns 0xffff for a bad block, and 0x400 for the
7328c2ecf20Sopenharmony_ci			 * CIS block. (Is this true for cards 8MB or less??)
7338c2ecf20Sopenharmony_ci			 * Record these in the physical to logical map
7348c2ecf20Sopenharmony_ci			 */
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci		info->pba_to_lba[i] = lba;
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci		if (lba >= max_lba) {
7398c2ecf20Sopenharmony_ci			continue;
7408c2ecf20Sopenharmony_ci		}
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci		if (info->lba_to_pba[lba + zone * 1000] != NOT_ALLOCATED &&
7438c2ecf20Sopenharmony_ci		    !info->force_read_only) {
7448c2ecf20Sopenharmony_ci			printk(KERN_WARNING
7458c2ecf20Sopenharmony_ci			       "sddr55: map inconsistency at LBA %04X\n",
7468c2ecf20Sopenharmony_ci			       lba + zone * 1000);
7478c2ecf20Sopenharmony_ci			info->force_read_only = 1;
7488c2ecf20Sopenharmony_ci		}
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci		if (lba<0x10 || (lba>=0x3E0 && lba<0x3EF))
7518c2ecf20Sopenharmony_ci			usb_stor_dbg(us, "LBA %04X <-> PBA %04X\n", lba, i);
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci		info->lba_to_pba[lba + zone * 1000] = i;
7548c2ecf20Sopenharmony_ci	}
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci	kfree(buffer);
7578c2ecf20Sopenharmony_ci	return 0;
7588c2ecf20Sopenharmony_ci}
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_cistatic void sddr55_card_info_destructor(void *extra) {
7628c2ecf20Sopenharmony_ci	struct sddr55_card_info *info = (struct sddr55_card_info *)extra;
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	if (!extra)
7658c2ecf20Sopenharmony_ci		return;
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci	kfree(info->lba_to_pba);
7688c2ecf20Sopenharmony_ci	kfree(info->pba_to_lba);
7698c2ecf20Sopenharmony_ci}
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci/*
7738c2ecf20Sopenharmony_ci * Transport for the Sandisk SDDR-55
7748c2ecf20Sopenharmony_ci */
7758c2ecf20Sopenharmony_cistatic int sddr55_transport(struct scsi_cmnd *srb, struct us_data *us)
7768c2ecf20Sopenharmony_ci{
7778c2ecf20Sopenharmony_ci	int result;
7788c2ecf20Sopenharmony_ci	static unsigned char inquiry_response[8] = {
7798c2ecf20Sopenharmony_ci		0x00, 0x80, 0x00, 0x02, 0x1F, 0x00, 0x00, 0x00
7808c2ecf20Sopenharmony_ci	};
7818c2ecf20Sopenharmony_ci 	// write-protected for now, no block descriptor support
7828c2ecf20Sopenharmony_ci	static unsigned char mode_page_01[20] = {
7838c2ecf20Sopenharmony_ci		0x0, 0x12, 0x00, 0x80, 0x0, 0x0, 0x0, 0x0,
7848c2ecf20Sopenharmony_ci		0x01, 0x0A,
7858c2ecf20Sopenharmony_ci		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
7868c2ecf20Sopenharmony_ci	};
7878c2ecf20Sopenharmony_ci	unsigned char *ptr = us->iobuf;
7888c2ecf20Sopenharmony_ci	unsigned long capacity;
7898c2ecf20Sopenharmony_ci	unsigned int lba;
7908c2ecf20Sopenharmony_ci	unsigned int pba;
7918c2ecf20Sopenharmony_ci	unsigned int page;
7928c2ecf20Sopenharmony_ci	unsigned short pages;
7938c2ecf20Sopenharmony_ci	struct sddr55_card_info *info;
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci	if (!us->extra) {
7968c2ecf20Sopenharmony_ci		us->extra = kzalloc(
7978c2ecf20Sopenharmony_ci			sizeof(struct sddr55_card_info), GFP_NOIO);
7988c2ecf20Sopenharmony_ci		if (!us->extra)
7998c2ecf20Sopenharmony_ci			return USB_STOR_TRANSPORT_ERROR;
8008c2ecf20Sopenharmony_ci		us->extra_destructor = sddr55_card_info_destructor;
8018c2ecf20Sopenharmony_ci	}
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	info = (struct sddr55_card_info *)(us->extra);
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	if (srb->cmnd[0] == REQUEST_SENSE) {
8068c2ecf20Sopenharmony_ci		usb_stor_dbg(us, "request sense %02x/%02x/%02x\n",
8078c2ecf20Sopenharmony_ci			     info->sense_data[2],
8088c2ecf20Sopenharmony_ci			     info->sense_data[12],
8098c2ecf20Sopenharmony_ci			     info->sense_data[13]);
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ci		memcpy (ptr, info->sense_data, sizeof info->sense_data);
8128c2ecf20Sopenharmony_ci		ptr[0] = 0x70;
8138c2ecf20Sopenharmony_ci		ptr[7] = 11;
8148c2ecf20Sopenharmony_ci		usb_stor_set_xfer_buf (ptr, sizeof info->sense_data, srb);
8158c2ecf20Sopenharmony_ci		memset (info->sense_data, 0, sizeof info->sense_data);
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci		return USB_STOR_TRANSPORT_GOOD;
8188c2ecf20Sopenharmony_ci	}
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci	memset (info->sense_data, 0, sizeof info->sense_data);
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci	/*
8238c2ecf20Sopenharmony_ci	 * Dummy up a response for INQUIRY since SDDR55 doesn't
8248c2ecf20Sopenharmony_ci	 * respond to INQUIRY commands
8258c2ecf20Sopenharmony_ci	 */
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci	if (srb->cmnd[0] == INQUIRY) {
8288c2ecf20Sopenharmony_ci		memcpy(ptr, inquiry_response, 8);
8298c2ecf20Sopenharmony_ci		fill_inquiry_response(us, ptr, 36);
8308c2ecf20Sopenharmony_ci		return USB_STOR_TRANSPORT_GOOD;
8318c2ecf20Sopenharmony_ci	}
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci	/*
8348c2ecf20Sopenharmony_ci	 * only check card status if the map isn't allocated, ie no card seen yet
8358c2ecf20Sopenharmony_ci	 * or if it's been over half a second since we last accessed it
8368c2ecf20Sopenharmony_ci	 */
8378c2ecf20Sopenharmony_ci	if (info->lba_to_pba == NULL || time_after(jiffies, info->last_access + HZ/2)) {
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci		/* check to see if a card is fitted */
8408c2ecf20Sopenharmony_ci		result = sddr55_status (us);
8418c2ecf20Sopenharmony_ci		if (result) {
8428c2ecf20Sopenharmony_ci			result = sddr55_status (us);
8438c2ecf20Sopenharmony_ci			if (!result) {
8448c2ecf20Sopenharmony_ci			set_sense_info (6, 0x28, 0);	/* new media, set unit attention, not ready to ready */
8458c2ecf20Sopenharmony_ci			}
8468c2ecf20Sopenharmony_ci			return USB_STOR_TRANSPORT_FAILED;
8478c2ecf20Sopenharmony_ci		}
8488c2ecf20Sopenharmony_ci	}
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci	/*
8518c2ecf20Sopenharmony_ci	 * if we detected a problem with the map when writing,
8528c2ecf20Sopenharmony_ci	 * don't allow any more access
8538c2ecf20Sopenharmony_ci	 */
8548c2ecf20Sopenharmony_ci	if (info->fatal_error) {
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci		set_sense_info (3, 0x31, 0);
8578c2ecf20Sopenharmony_ci		return USB_STOR_TRANSPORT_FAILED;
8588c2ecf20Sopenharmony_ci	}
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_ci	if (srb->cmnd[0] == READ_CAPACITY) {
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci		capacity = sddr55_get_capacity(us);
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_ci		if (!capacity) {
8658c2ecf20Sopenharmony_ci			set_sense_info (3, 0x30, 0); /* incompatible medium */
8668c2ecf20Sopenharmony_ci			return USB_STOR_TRANSPORT_FAILED;
8678c2ecf20Sopenharmony_ci		}
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci		info->capacity = capacity;
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci		/*
8728c2ecf20Sopenharmony_ci		 * figure out the maximum logical block number, allowing for
8738c2ecf20Sopenharmony_ci		 * the fact that only 250 out of every 256 are used
8748c2ecf20Sopenharmony_ci		 */
8758c2ecf20Sopenharmony_ci		info->max_log_blks = ((info->capacity >> (info->pageshift + info->blockshift)) / 256) * 250;
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci		/*
8788c2ecf20Sopenharmony_ci		 * Last page in the card, adjust as we only use 250 out of
8798c2ecf20Sopenharmony_ci		 * every 256 pages
8808c2ecf20Sopenharmony_ci		 */
8818c2ecf20Sopenharmony_ci		capacity = (capacity / 256) * 250;
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci		capacity /= PAGESIZE;
8848c2ecf20Sopenharmony_ci		capacity--;
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ci		((__be32 *) ptr)[0] = cpu_to_be32(capacity);
8878c2ecf20Sopenharmony_ci		((__be32 *) ptr)[1] = cpu_to_be32(PAGESIZE);
8888c2ecf20Sopenharmony_ci		usb_stor_set_xfer_buf(ptr, 8, srb);
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci		sddr55_read_map(us);
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci		return USB_STOR_TRANSPORT_GOOD;
8938c2ecf20Sopenharmony_ci	}
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci	if (srb->cmnd[0] == MODE_SENSE_10) {
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_ci		memcpy(ptr, mode_page_01, sizeof mode_page_01);
8988c2ecf20Sopenharmony_ci		ptr[3] = (info->read_only || info->force_read_only) ? 0x80 : 0;
8998c2ecf20Sopenharmony_ci		usb_stor_set_xfer_buf(ptr, sizeof(mode_page_01), srb);
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci		if ( (srb->cmnd[2] & 0x3F) == 0x01 ) {
9028c2ecf20Sopenharmony_ci			usb_stor_dbg(us, "Dummy up request for mode page 1\n");
9038c2ecf20Sopenharmony_ci			return USB_STOR_TRANSPORT_GOOD;
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci		} else if ( (srb->cmnd[2] & 0x3F) == 0x3F ) {
9068c2ecf20Sopenharmony_ci			usb_stor_dbg(us, "Dummy up request for all mode pages\n");
9078c2ecf20Sopenharmony_ci			return USB_STOR_TRANSPORT_GOOD;
9088c2ecf20Sopenharmony_ci		}
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_ci		set_sense_info (5, 0x24, 0);	/* invalid field in command */
9118c2ecf20Sopenharmony_ci		return USB_STOR_TRANSPORT_FAILED;
9128c2ecf20Sopenharmony_ci	}
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci	if (srb->cmnd[0] == ALLOW_MEDIUM_REMOVAL) {
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci		usb_stor_dbg(us, "%s medium removal. Not that I can do anything about it...\n",
9178c2ecf20Sopenharmony_ci			     (srb->cmnd[4]&0x03) ? "Prevent" : "Allow");
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci		return USB_STOR_TRANSPORT_GOOD;
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_ci	}
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_ci	if (srb->cmnd[0] == READ_10 || srb->cmnd[0] == WRITE_10) {
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci		page = short_pack(srb->cmnd[3], srb->cmnd[2]);
9268c2ecf20Sopenharmony_ci		page <<= 16;
9278c2ecf20Sopenharmony_ci		page |= short_pack(srb->cmnd[5], srb->cmnd[4]);
9288c2ecf20Sopenharmony_ci		pages = short_pack(srb->cmnd[8], srb->cmnd[7]);
9298c2ecf20Sopenharmony_ci
9308c2ecf20Sopenharmony_ci		page <<= info->smallpageshift;
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_ci		// convert page to block and page-within-block
9338c2ecf20Sopenharmony_ci
9348c2ecf20Sopenharmony_ci		lba = page >> info->blockshift;
9358c2ecf20Sopenharmony_ci		page = page & info->blockmask;
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_ci		// locate physical block corresponding to logical block
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_ci		if (lba >= info->max_log_blks) {
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_ci			usb_stor_dbg(us, "Error: Requested LBA %04X exceeds maximum block %04X\n",
9428c2ecf20Sopenharmony_ci				     lba, info->max_log_blks - 1);
9438c2ecf20Sopenharmony_ci
9448c2ecf20Sopenharmony_ci			set_sense_info (5, 0x24, 0);	/* invalid field in command */
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci			return USB_STOR_TRANSPORT_FAILED;
9478c2ecf20Sopenharmony_ci		}
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_ci		pba = info->lba_to_pba[lba];
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_ci		if (srb->cmnd[0] == WRITE_10) {
9528c2ecf20Sopenharmony_ci			usb_stor_dbg(us, "WRITE_10: write block %04X (LBA %04X) page %01X pages %d\n",
9538c2ecf20Sopenharmony_ci				     pba, lba, page, pages);
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci			return sddr55_write_data(us, lba, page, pages);
9568c2ecf20Sopenharmony_ci		} else {
9578c2ecf20Sopenharmony_ci			usb_stor_dbg(us, "READ_10: read block %04X (LBA %04X) page %01X pages %d\n",
9588c2ecf20Sopenharmony_ci				     pba, lba, page, pages);
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_ci			return sddr55_read_data(us, lba, page, pages);
9618c2ecf20Sopenharmony_ci		}
9628c2ecf20Sopenharmony_ci	}
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_ci	if (srb->cmnd[0] == TEST_UNIT_READY) {
9668c2ecf20Sopenharmony_ci		return USB_STOR_TRANSPORT_GOOD;
9678c2ecf20Sopenharmony_ci	}
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_ci	if (srb->cmnd[0] == START_STOP) {
9708c2ecf20Sopenharmony_ci		return USB_STOR_TRANSPORT_GOOD;
9718c2ecf20Sopenharmony_ci	}
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci	set_sense_info (5, 0x20, 0);	/* illegal command */
9748c2ecf20Sopenharmony_ci
9758c2ecf20Sopenharmony_ci	return USB_STOR_TRANSPORT_FAILED; // FIXME: sense buffer?
9768c2ecf20Sopenharmony_ci}
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_cistatic struct scsi_host_template sddr55_host_template;
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_cistatic int sddr55_probe(struct usb_interface *intf,
9818c2ecf20Sopenharmony_ci			 const struct usb_device_id *id)
9828c2ecf20Sopenharmony_ci{
9838c2ecf20Sopenharmony_ci	struct us_data *us;
9848c2ecf20Sopenharmony_ci	int result;
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci	result = usb_stor_probe1(&us, intf, id,
9878c2ecf20Sopenharmony_ci			(id - sddr55_usb_ids) + sddr55_unusual_dev_list,
9888c2ecf20Sopenharmony_ci			&sddr55_host_template);
9898c2ecf20Sopenharmony_ci	if (result)
9908c2ecf20Sopenharmony_ci		return result;
9918c2ecf20Sopenharmony_ci
9928c2ecf20Sopenharmony_ci	us->transport_name = "SDDR55";
9938c2ecf20Sopenharmony_ci	us->transport = sddr55_transport;
9948c2ecf20Sopenharmony_ci	us->transport_reset = sddr55_reset;
9958c2ecf20Sopenharmony_ci	us->max_lun = 0;
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci	result = usb_stor_probe2(us);
9988c2ecf20Sopenharmony_ci	return result;
9998c2ecf20Sopenharmony_ci}
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_cistatic struct usb_driver sddr55_driver = {
10028c2ecf20Sopenharmony_ci	.name =		DRV_NAME,
10038c2ecf20Sopenharmony_ci	.probe =	sddr55_probe,
10048c2ecf20Sopenharmony_ci	.disconnect =	usb_stor_disconnect,
10058c2ecf20Sopenharmony_ci	.suspend =	usb_stor_suspend,
10068c2ecf20Sopenharmony_ci	.resume =	usb_stor_resume,
10078c2ecf20Sopenharmony_ci	.reset_resume =	usb_stor_reset_resume,
10088c2ecf20Sopenharmony_ci	.pre_reset =	usb_stor_pre_reset,
10098c2ecf20Sopenharmony_ci	.post_reset =	usb_stor_post_reset,
10108c2ecf20Sopenharmony_ci	.id_table =	sddr55_usb_ids,
10118c2ecf20Sopenharmony_ci	.soft_unbind =	1,
10128c2ecf20Sopenharmony_ci	.no_dynamic_id = 1,
10138c2ecf20Sopenharmony_ci};
10148c2ecf20Sopenharmony_ci
10158c2ecf20Sopenharmony_cimodule_usb_stor_driver(sddr55_driver, sddr55_host_template, DRV_NAME);
1016