18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci pt.c (c) 1998 Grant R. Guenther <grant@torque.net> 38c2ecf20Sopenharmony_ci Under the terms of the GNU General Public License. 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci This is the high-level driver for parallel port ATAPI tape 68c2ecf20Sopenharmony_ci drives based on chips supported by the paride module. 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci The driver implements both rewinding and non-rewinding 98c2ecf20Sopenharmony_ci devices, filemarks, and the rewind ioctl. It allocates 108c2ecf20Sopenharmony_ci a small internal "bounce buffer" for each open device, but 118c2ecf20Sopenharmony_ci otherwise expects buffering and blocking to be done at the 128c2ecf20Sopenharmony_ci user level. As with most block-structured tapes, short 138c2ecf20Sopenharmony_ci writes are padded to full tape blocks, so reading back a file 148c2ecf20Sopenharmony_ci may return more data than was actually written. 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci By default, the driver will autoprobe for a single parallel 178c2ecf20Sopenharmony_ci port ATAPI tape drive, but if their individual parameters are 188c2ecf20Sopenharmony_ci specified, the driver can handle up to 4 drives. 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci The rewinding devices are named /dev/pt0, /dev/pt1, ... 218c2ecf20Sopenharmony_ci while the non-rewinding devices are /dev/npt0, /dev/npt1, etc. 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci The behaviour of the pt driver can be altered by setting 248c2ecf20Sopenharmony_ci some parameters from the insmod command line. The following 258c2ecf20Sopenharmony_ci parameters are adjustable: 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci drive0 These four arguments can be arrays of 288c2ecf20Sopenharmony_ci drive1 1-6 integers as follows: 298c2ecf20Sopenharmony_ci drive2 308c2ecf20Sopenharmony_ci drive3 <prt>,<pro>,<uni>,<mod>,<slv>,<dly> 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci Where, 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci <prt> is the base of the parallel port address for 358c2ecf20Sopenharmony_ci the corresponding drive. (required) 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci <pro> is the protocol number for the adapter that 388c2ecf20Sopenharmony_ci supports this drive. These numbers are 398c2ecf20Sopenharmony_ci logged by 'paride' when the protocol modules 408c2ecf20Sopenharmony_ci are initialised. (0 if not given) 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci <uni> for those adapters that support chained 438c2ecf20Sopenharmony_ci devices, this is the unit selector for the 448c2ecf20Sopenharmony_ci chain of devices on the given port. It should 458c2ecf20Sopenharmony_ci be zero for devices that don't support chaining. 468c2ecf20Sopenharmony_ci (0 if not given) 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci <mod> this can be -1 to choose the best mode, or one 498c2ecf20Sopenharmony_ci of the mode numbers supported by the adapter. 508c2ecf20Sopenharmony_ci (-1 if not given) 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci <slv> ATAPI devices can be jumpered to master or slave. 538c2ecf20Sopenharmony_ci Set this to 0 to choose the master drive, 1 to 548c2ecf20Sopenharmony_ci choose the slave, -1 (the default) to choose the 558c2ecf20Sopenharmony_ci first drive found. 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci <dly> some parallel ports require the driver to 588c2ecf20Sopenharmony_ci go more slowly. -1 sets a default value that 598c2ecf20Sopenharmony_ci should work with the chosen protocol. Otherwise, 608c2ecf20Sopenharmony_ci set this to a small integer, the larger it is 618c2ecf20Sopenharmony_ci the slower the port i/o. In some cases, setting 628c2ecf20Sopenharmony_ci this to zero will speed up the device. (default -1) 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci major You may use this parameter to override the 658c2ecf20Sopenharmony_ci default major number (96) that this driver 668c2ecf20Sopenharmony_ci will use. Be sure to change the device 678c2ecf20Sopenharmony_ci name as well. 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci name This parameter is a character string that 708c2ecf20Sopenharmony_ci contains the name the kernel will use for this 718c2ecf20Sopenharmony_ci device (in /proc output, for instance). 728c2ecf20Sopenharmony_ci (default "pt"). 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci verbose This parameter controls the amount of logging 758c2ecf20Sopenharmony_ci that the driver will do. Set it to 0 for 768c2ecf20Sopenharmony_ci normal operation, 1 to see autoprobe progress 778c2ecf20Sopenharmony_ci messages, or 2 to see additional debugging 788c2ecf20Sopenharmony_ci output. (default 0) 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci If this driver is built into the kernel, you can use 818c2ecf20Sopenharmony_ci the following command line parameters, with the same values 828c2ecf20Sopenharmony_ci as the corresponding module parameters listed above: 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci pt.drive0 858c2ecf20Sopenharmony_ci pt.drive1 868c2ecf20Sopenharmony_ci pt.drive2 878c2ecf20Sopenharmony_ci pt.drive3 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci In addition, you can use the parameter pt.disable to disable 908c2ecf20Sopenharmony_ci the driver entirely. 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci*/ 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci/* Changes: 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci 1.01 GRG 1998.05.06 Round up transfer size, fix ready_wait, 978c2ecf20Sopenharmony_ci loosed interpretation of ATAPI standard 988c2ecf20Sopenharmony_ci for clearing error status. 998c2ecf20Sopenharmony_ci Eliminate sti(); 1008c2ecf20Sopenharmony_ci 1.02 GRG 1998.06.16 Eliminate an Ugh. 1018c2ecf20Sopenharmony_ci 1.03 GRG 1998.08.15 Adjusted PT_TMO, use HZ in loop timing, 1028c2ecf20Sopenharmony_ci extra debugging 1038c2ecf20Sopenharmony_ci 1.04 GRG 1998.09.24 Repair minor coding error, added jumbo support 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci*/ 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci#define PT_VERSION "1.04" 1088c2ecf20Sopenharmony_ci#define PT_MAJOR 96 1098c2ecf20Sopenharmony_ci#define PT_NAME "pt" 1108c2ecf20Sopenharmony_ci#define PT_UNITS 4 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci#include <linux/types.h> 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci/* Here are things one can override from the insmod command. 1158c2ecf20Sopenharmony_ci Most are autoprobed by paride unless set here. Verbose is on 1168c2ecf20Sopenharmony_ci by default. 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci*/ 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic int verbose = 0; 1218c2ecf20Sopenharmony_cistatic int major = PT_MAJOR; 1228c2ecf20Sopenharmony_cistatic char *name = PT_NAME; 1238c2ecf20Sopenharmony_cistatic int disable = 0; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic int drive0[6] = { 0, 0, 0, -1, -1, -1 }; 1268c2ecf20Sopenharmony_cistatic int drive1[6] = { 0, 0, 0, -1, -1, -1 }; 1278c2ecf20Sopenharmony_cistatic int drive2[6] = { 0, 0, 0, -1, -1, -1 }; 1288c2ecf20Sopenharmony_cistatic int drive3[6] = { 0, 0, 0, -1, -1, -1 }; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic int (*drives[4])[6] = {&drive0, &drive1, &drive2, &drive3}; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci#define D_PRT 0 1338c2ecf20Sopenharmony_ci#define D_PRO 1 1348c2ecf20Sopenharmony_ci#define D_UNI 2 1358c2ecf20Sopenharmony_ci#define D_MOD 3 1368c2ecf20Sopenharmony_ci#define D_SLV 4 1378c2ecf20Sopenharmony_ci#define D_DLY 5 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci#define DU (*drives[unit]) 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci/* end of parameters */ 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci#include <linux/module.h> 1448c2ecf20Sopenharmony_ci#include <linux/init.h> 1458c2ecf20Sopenharmony_ci#include <linux/fs.h> 1468c2ecf20Sopenharmony_ci#include <linux/delay.h> 1478c2ecf20Sopenharmony_ci#include <linux/slab.h> 1488c2ecf20Sopenharmony_ci#include <linux/mtio.h> 1498c2ecf20Sopenharmony_ci#include <linux/device.h> 1508c2ecf20Sopenharmony_ci#include <linux/sched.h> /* current, TASK_*, schedule_timeout() */ 1518c2ecf20Sopenharmony_ci#include <linux/mutex.h> 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cimodule_param(verbose, int, 0); 1568c2ecf20Sopenharmony_cimodule_param(major, int, 0); 1578c2ecf20Sopenharmony_cimodule_param(name, charp, 0); 1588c2ecf20Sopenharmony_cimodule_param_array(drive0, int, NULL, 0); 1598c2ecf20Sopenharmony_cimodule_param_array(drive1, int, NULL, 0); 1608c2ecf20Sopenharmony_cimodule_param_array(drive2, int, NULL, 0); 1618c2ecf20Sopenharmony_cimodule_param_array(drive3, int, NULL, 0); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci#include "paride.h" 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci#define PT_MAX_RETRIES 5 1668c2ecf20Sopenharmony_ci#define PT_TMO 3000 /* interrupt timeout in jiffies */ 1678c2ecf20Sopenharmony_ci#define PT_SPIN_DEL 50 /* spin delay in micro-seconds */ 1688c2ecf20Sopenharmony_ci#define PT_RESET_TMO 30 /* 30 seconds */ 1698c2ecf20Sopenharmony_ci#define PT_READY_TMO 60 /* 60 seconds */ 1708c2ecf20Sopenharmony_ci#define PT_REWIND_TMO 1200 /* 20 minutes */ 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci#define PT_SPIN ((1000000/(HZ*PT_SPIN_DEL))*PT_TMO) 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci#define STAT_ERR 0x00001 1758c2ecf20Sopenharmony_ci#define STAT_INDEX 0x00002 1768c2ecf20Sopenharmony_ci#define STAT_ECC 0x00004 1778c2ecf20Sopenharmony_ci#define STAT_DRQ 0x00008 1788c2ecf20Sopenharmony_ci#define STAT_SEEK 0x00010 1798c2ecf20Sopenharmony_ci#define STAT_WRERR 0x00020 1808c2ecf20Sopenharmony_ci#define STAT_READY 0x00040 1818c2ecf20Sopenharmony_ci#define STAT_BUSY 0x00080 1828c2ecf20Sopenharmony_ci#define STAT_SENSE 0x1f000 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci#define ATAPI_TEST_READY 0x00 1858c2ecf20Sopenharmony_ci#define ATAPI_REWIND 0x01 1868c2ecf20Sopenharmony_ci#define ATAPI_REQ_SENSE 0x03 1878c2ecf20Sopenharmony_ci#define ATAPI_READ_6 0x08 1888c2ecf20Sopenharmony_ci#define ATAPI_WRITE_6 0x0a 1898c2ecf20Sopenharmony_ci#define ATAPI_WFM 0x10 1908c2ecf20Sopenharmony_ci#define ATAPI_IDENTIFY 0x12 1918c2ecf20Sopenharmony_ci#define ATAPI_MODE_SENSE 0x1a 1928c2ecf20Sopenharmony_ci#define ATAPI_LOG_SENSE 0x4d 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(pt_mutex); 1958c2ecf20Sopenharmony_cistatic int pt_open(struct inode *inode, struct file *file); 1968c2ecf20Sopenharmony_cistatic long pt_ioctl(struct file *file, unsigned int cmd, unsigned long arg); 1978c2ecf20Sopenharmony_cistatic int pt_release(struct inode *inode, struct file *file); 1988c2ecf20Sopenharmony_cistatic ssize_t pt_read(struct file *filp, char __user *buf, 1998c2ecf20Sopenharmony_ci size_t count, loff_t * ppos); 2008c2ecf20Sopenharmony_cistatic ssize_t pt_write(struct file *filp, const char __user *buf, 2018c2ecf20Sopenharmony_ci size_t count, loff_t * ppos); 2028c2ecf20Sopenharmony_cistatic int pt_detect(void); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci/* bits in tape->flags */ 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci#define PT_MEDIA 1 2078c2ecf20Sopenharmony_ci#define PT_WRITE_OK 2 2088c2ecf20Sopenharmony_ci#define PT_REWIND 4 2098c2ecf20Sopenharmony_ci#define PT_WRITING 8 2108c2ecf20Sopenharmony_ci#define PT_READING 16 2118c2ecf20Sopenharmony_ci#define PT_EOF 32 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci#define PT_NAMELEN 8 2148c2ecf20Sopenharmony_ci#define PT_BUFSIZE 16384 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistruct pt_unit { 2178c2ecf20Sopenharmony_ci struct pi_adapter pia; /* interface to paride layer */ 2188c2ecf20Sopenharmony_ci struct pi_adapter *pi; 2198c2ecf20Sopenharmony_ci int flags; /* various state flags */ 2208c2ecf20Sopenharmony_ci int last_sense; /* result of last request sense */ 2218c2ecf20Sopenharmony_ci int drive; /* drive */ 2228c2ecf20Sopenharmony_ci atomic_t available; /* 1 if access is available 0 otherwise */ 2238c2ecf20Sopenharmony_ci int bs; /* block size */ 2248c2ecf20Sopenharmony_ci int capacity; /* Size of tape in KB */ 2258c2ecf20Sopenharmony_ci int present; /* device present ? */ 2268c2ecf20Sopenharmony_ci char *bufptr; 2278c2ecf20Sopenharmony_ci char name[PT_NAMELEN]; /* pf0, pf1, ... */ 2288c2ecf20Sopenharmony_ci}; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistatic int pt_identify(struct pt_unit *tape); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_cistatic struct pt_unit pt[PT_UNITS]; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistatic char pt_scratch[512]; /* scratch block buffer */ 2358c2ecf20Sopenharmony_cistatic void *par_drv; /* reference of parport driver */ 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci/* kernel glue structures */ 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic const struct file_operations pt_fops = { 2408c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 2418c2ecf20Sopenharmony_ci .read = pt_read, 2428c2ecf20Sopenharmony_ci .write = pt_write, 2438c2ecf20Sopenharmony_ci .unlocked_ioctl = pt_ioctl, 2448c2ecf20Sopenharmony_ci .open = pt_open, 2458c2ecf20Sopenharmony_ci .release = pt_release, 2468c2ecf20Sopenharmony_ci .llseek = noop_llseek, 2478c2ecf20Sopenharmony_ci}; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci/* sysfs class support */ 2508c2ecf20Sopenharmony_cistatic struct class *pt_class; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistatic inline int status_reg(struct pi_adapter *pi) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci return pi_read_regr(pi, 1, 6); 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic inline int read_reg(struct pi_adapter *pi, int reg) 2588c2ecf20Sopenharmony_ci{ 2598c2ecf20Sopenharmony_ci return pi_read_regr(pi, 0, reg); 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_cistatic inline void write_reg(struct pi_adapter *pi, int reg, int val) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci pi_write_regr(pi, 0, reg, val); 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic inline u8 DRIVE(struct pt_unit *tape) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci return 0xa0+0x10*tape->drive; 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistatic int pt_wait(struct pt_unit *tape, int go, int stop, char *fun, char *msg) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci int j, r, e, s, p; 2758c2ecf20Sopenharmony_ci struct pi_adapter *pi = tape->pi; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci j = 0; 2788c2ecf20Sopenharmony_ci while ((((r = status_reg(pi)) & go) || (stop && (!(r & stop)))) 2798c2ecf20Sopenharmony_ci && (j++ < PT_SPIN)) 2808c2ecf20Sopenharmony_ci udelay(PT_SPIN_DEL); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci if ((r & (STAT_ERR & stop)) || (j > PT_SPIN)) { 2838c2ecf20Sopenharmony_ci s = read_reg(pi, 7); 2848c2ecf20Sopenharmony_ci e = read_reg(pi, 1); 2858c2ecf20Sopenharmony_ci p = read_reg(pi, 2); 2868c2ecf20Sopenharmony_ci if (j > PT_SPIN) 2878c2ecf20Sopenharmony_ci e |= 0x100; 2888c2ecf20Sopenharmony_ci if (fun) 2898c2ecf20Sopenharmony_ci printk("%s: %s %s: alt=0x%x stat=0x%x err=0x%x" 2908c2ecf20Sopenharmony_ci " loop=%d phase=%d\n", 2918c2ecf20Sopenharmony_ci tape->name, fun, msg, r, s, e, j, p); 2928c2ecf20Sopenharmony_ci return (e << 8) + s; 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci return 0; 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistatic int pt_command(struct pt_unit *tape, char *cmd, int dlen, char *fun) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci struct pi_adapter *pi = tape->pi; 3008c2ecf20Sopenharmony_ci pi_connect(pi); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci write_reg(pi, 6, DRIVE(tape)); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci if (pt_wait(tape, STAT_BUSY | STAT_DRQ, 0, fun, "before command")) { 3058c2ecf20Sopenharmony_ci pi_disconnect(pi); 3068c2ecf20Sopenharmony_ci return -1; 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci write_reg(pi, 4, dlen % 256); 3108c2ecf20Sopenharmony_ci write_reg(pi, 5, dlen / 256); 3118c2ecf20Sopenharmony_ci write_reg(pi, 7, 0xa0); /* ATAPI packet command */ 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci if (pt_wait(tape, STAT_BUSY, STAT_DRQ, fun, "command DRQ")) { 3148c2ecf20Sopenharmony_ci pi_disconnect(pi); 3158c2ecf20Sopenharmony_ci return -1; 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci if (read_reg(pi, 2) != 1) { 3198c2ecf20Sopenharmony_ci printk("%s: %s: command phase error\n", tape->name, fun); 3208c2ecf20Sopenharmony_ci pi_disconnect(pi); 3218c2ecf20Sopenharmony_ci return -1; 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci pi_write_block(pi, cmd, 12); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci return 0; 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_cistatic int pt_completion(struct pt_unit *tape, char *buf, char *fun) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci struct pi_adapter *pi = tape->pi; 3328c2ecf20Sopenharmony_ci int r, s, n, p; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci r = pt_wait(tape, STAT_BUSY, STAT_DRQ | STAT_READY | STAT_ERR, 3358c2ecf20Sopenharmony_ci fun, "completion"); 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci if (read_reg(pi, 7) & STAT_DRQ) { 3388c2ecf20Sopenharmony_ci n = (((read_reg(pi, 4) + 256 * read_reg(pi, 5)) + 3398c2ecf20Sopenharmony_ci 3) & 0xfffc); 3408c2ecf20Sopenharmony_ci p = read_reg(pi, 2) & 3; 3418c2ecf20Sopenharmony_ci if (p == 0) 3428c2ecf20Sopenharmony_ci pi_write_block(pi, buf, n); 3438c2ecf20Sopenharmony_ci if (p == 2) 3448c2ecf20Sopenharmony_ci pi_read_block(pi, buf, n); 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci s = pt_wait(tape, STAT_BUSY, STAT_READY | STAT_ERR, fun, "data done"); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci pi_disconnect(pi); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci return (r ? r : s); 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic void pt_req_sense(struct pt_unit *tape, int quiet) 3558c2ecf20Sopenharmony_ci{ 3568c2ecf20Sopenharmony_ci char rs_cmd[12] = { ATAPI_REQ_SENSE, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0 }; 3578c2ecf20Sopenharmony_ci char buf[16]; 3588c2ecf20Sopenharmony_ci int r; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci r = pt_command(tape, rs_cmd, 16, "Request sense"); 3618c2ecf20Sopenharmony_ci mdelay(1); 3628c2ecf20Sopenharmony_ci if (!r) 3638c2ecf20Sopenharmony_ci pt_completion(tape, buf, "Request sense"); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci tape->last_sense = -1; 3668c2ecf20Sopenharmony_ci if (!r) { 3678c2ecf20Sopenharmony_ci if (!quiet) 3688c2ecf20Sopenharmony_ci printk("%s: Sense key: %x, ASC: %x, ASQ: %x\n", 3698c2ecf20Sopenharmony_ci tape->name, buf[2] & 0xf, buf[12], buf[13]); 3708c2ecf20Sopenharmony_ci tape->last_sense = (buf[2] & 0xf) | ((buf[12] & 0xff) << 8) 3718c2ecf20Sopenharmony_ci | ((buf[13] & 0xff) << 16); 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cistatic int pt_atapi(struct pt_unit *tape, char *cmd, int dlen, char *buf, char *fun) 3768c2ecf20Sopenharmony_ci{ 3778c2ecf20Sopenharmony_ci int r; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci r = pt_command(tape, cmd, dlen, fun); 3808c2ecf20Sopenharmony_ci mdelay(1); 3818c2ecf20Sopenharmony_ci if (!r) 3828c2ecf20Sopenharmony_ci r = pt_completion(tape, buf, fun); 3838c2ecf20Sopenharmony_ci if (r) 3848c2ecf20Sopenharmony_ci pt_req_sense(tape, !fun); 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci return r; 3878c2ecf20Sopenharmony_ci} 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_cistatic void pt_sleep(int cs) 3908c2ecf20Sopenharmony_ci{ 3918c2ecf20Sopenharmony_ci schedule_timeout_interruptible(cs); 3928c2ecf20Sopenharmony_ci} 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_cistatic int pt_poll_dsc(struct pt_unit *tape, int pause, int tmo, char *msg) 3958c2ecf20Sopenharmony_ci{ 3968c2ecf20Sopenharmony_ci struct pi_adapter *pi = tape->pi; 3978c2ecf20Sopenharmony_ci int k, e, s; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci k = 0; 4008c2ecf20Sopenharmony_ci e = 0; 4018c2ecf20Sopenharmony_ci s = 0; 4028c2ecf20Sopenharmony_ci while (k < tmo) { 4038c2ecf20Sopenharmony_ci pt_sleep(pause); 4048c2ecf20Sopenharmony_ci k++; 4058c2ecf20Sopenharmony_ci pi_connect(pi); 4068c2ecf20Sopenharmony_ci write_reg(pi, 6, DRIVE(tape)); 4078c2ecf20Sopenharmony_ci s = read_reg(pi, 7); 4088c2ecf20Sopenharmony_ci e = read_reg(pi, 1); 4098c2ecf20Sopenharmony_ci pi_disconnect(pi); 4108c2ecf20Sopenharmony_ci if (s & (STAT_ERR | STAT_SEEK)) 4118c2ecf20Sopenharmony_ci break; 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci if ((k >= tmo) || (s & STAT_ERR)) { 4148c2ecf20Sopenharmony_ci if (k >= tmo) 4158c2ecf20Sopenharmony_ci printk("%s: %s DSC timeout\n", tape->name, msg); 4168c2ecf20Sopenharmony_ci else 4178c2ecf20Sopenharmony_ci printk("%s: %s stat=0x%x err=0x%x\n", tape->name, msg, s, 4188c2ecf20Sopenharmony_ci e); 4198c2ecf20Sopenharmony_ci pt_req_sense(tape, 0); 4208c2ecf20Sopenharmony_ci return 0; 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci return 1; 4238c2ecf20Sopenharmony_ci} 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_cistatic void pt_media_access_cmd(struct pt_unit *tape, int tmo, char *cmd, char *fun) 4268c2ecf20Sopenharmony_ci{ 4278c2ecf20Sopenharmony_ci if (pt_command(tape, cmd, 0, fun)) { 4288c2ecf20Sopenharmony_ci pt_req_sense(tape, 0); 4298c2ecf20Sopenharmony_ci return; 4308c2ecf20Sopenharmony_ci } 4318c2ecf20Sopenharmony_ci pi_disconnect(tape->pi); 4328c2ecf20Sopenharmony_ci pt_poll_dsc(tape, HZ, tmo, fun); 4338c2ecf20Sopenharmony_ci} 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_cistatic void pt_rewind(struct pt_unit *tape) 4368c2ecf20Sopenharmony_ci{ 4378c2ecf20Sopenharmony_ci char rw_cmd[12] = { ATAPI_REWIND, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci pt_media_access_cmd(tape, PT_REWIND_TMO, rw_cmd, "rewind"); 4408c2ecf20Sopenharmony_ci} 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cistatic void pt_write_fm(struct pt_unit *tape) 4438c2ecf20Sopenharmony_ci{ 4448c2ecf20Sopenharmony_ci char wm_cmd[12] = { ATAPI_WFM, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 }; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci pt_media_access_cmd(tape, PT_TMO, wm_cmd, "write filemark"); 4478c2ecf20Sopenharmony_ci} 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci#define DBMSG(msg) ((verbose>1)?(msg):NULL) 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_cistatic int pt_reset(struct pt_unit *tape) 4528c2ecf20Sopenharmony_ci{ 4538c2ecf20Sopenharmony_ci struct pi_adapter *pi = tape->pi; 4548c2ecf20Sopenharmony_ci int i, k, flg; 4558c2ecf20Sopenharmony_ci int expect[5] = { 1, 1, 1, 0x14, 0xeb }; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci pi_connect(pi); 4588c2ecf20Sopenharmony_ci write_reg(pi, 6, DRIVE(tape)); 4598c2ecf20Sopenharmony_ci write_reg(pi, 7, 8); 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci pt_sleep(20 * HZ / 1000); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci k = 0; 4648c2ecf20Sopenharmony_ci while ((k++ < PT_RESET_TMO) && (status_reg(pi) & STAT_BUSY)) 4658c2ecf20Sopenharmony_ci pt_sleep(HZ / 10); 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci flg = 1; 4688c2ecf20Sopenharmony_ci for (i = 0; i < 5; i++) 4698c2ecf20Sopenharmony_ci flg &= (read_reg(pi, i + 1) == expect[i]); 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci if (verbose) { 4728c2ecf20Sopenharmony_ci printk("%s: Reset (%d) signature = ", tape->name, k); 4738c2ecf20Sopenharmony_ci for (i = 0; i < 5; i++) 4748c2ecf20Sopenharmony_ci printk("%3x", read_reg(pi, i + 1)); 4758c2ecf20Sopenharmony_ci if (!flg) 4768c2ecf20Sopenharmony_ci printk(" (incorrect)"); 4778c2ecf20Sopenharmony_ci printk("\n"); 4788c2ecf20Sopenharmony_ci } 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci pi_disconnect(pi); 4818c2ecf20Sopenharmony_ci return flg - 1; 4828c2ecf20Sopenharmony_ci} 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_cistatic int pt_ready_wait(struct pt_unit *tape, int tmo) 4858c2ecf20Sopenharmony_ci{ 4868c2ecf20Sopenharmony_ci char tr_cmd[12] = { ATAPI_TEST_READY, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; 4878c2ecf20Sopenharmony_ci int k, p; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci k = 0; 4908c2ecf20Sopenharmony_ci while (k < tmo) { 4918c2ecf20Sopenharmony_ci tape->last_sense = 0; 4928c2ecf20Sopenharmony_ci pt_atapi(tape, tr_cmd, 0, NULL, DBMSG("test unit ready")); 4938c2ecf20Sopenharmony_ci p = tape->last_sense; 4948c2ecf20Sopenharmony_ci if (!p) 4958c2ecf20Sopenharmony_ci return 0; 4968c2ecf20Sopenharmony_ci if (!(((p & 0xffff) == 0x0402) || ((p & 0xff) == 6))) 4978c2ecf20Sopenharmony_ci return p; 4988c2ecf20Sopenharmony_ci k++; 4998c2ecf20Sopenharmony_ci pt_sleep(HZ); 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci return 0x000020; /* timeout */ 5028c2ecf20Sopenharmony_ci} 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_cistatic void xs(char *buf, char *targ, int offs, int len) 5058c2ecf20Sopenharmony_ci{ 5068c2ecf20Sopenharmony_ci int j, k, l; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci j = 0; 5098c2ecf20Sopenharmony_ci l = 0; 5108c2ecf20Sopenharmony_ci for (k = 0; k < len; k++) 5118c2ecf20Sopenharmony_ci if ((buf[k + offs] != 0x20) || (buf[k + offs] != l)) 5128c2ecf20Sopenharmony_ci l = targ[j++] = buf[k + offs]; 5138c2ecf20Sopenharmony_ci if (l == 0x20) 5148c2ecf20Sopenharmony_ci j--; 5158c2ecf20Sopenharmony_ci targ[j] = 0; 5168c2ecf20Sopenharmony_ci} 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_cistatic int xn(char *buf, int offs, int size) 5198c2ecf20Sopenharmony_ci{ 5208c2ecf20Sopenharmony_ci int v, k; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci v = 0; 5238c2ecf20Sopenharmony_ci for (k = 0; k < size; k++) 5248c2ecf20Sopenharmony_ci v = v * 256 + (buf[k + offs] & 0xff); 5258c2ecf20Sopenharmony_ci return v; 5268c2ecf20Sopenharmony_ci} 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_cistatic int pt_identify(struct pt_unit *tape) 5298c2ecf20Sopenharmony_ci{ 5308c2ecf20Sopenharmony_ci int dt, s; 5318c2ecf20Sopenharmony_ci char *ms[2] = { "master", "slave" }; 5328c2ecf20Sopenharmony_ci char mf[10], id[18]; 5338c2ecf20Sopenharmony_ci char id_cmd[12] = { ATAPI_IDENTIFY, 0, 0, 0, 36, 0, 0, 0, 0, 0, 0, 0 }; 5348c2ecf20Sopenharmony_ci char ms_cmd[12] = 5358c2ecf20Sopenharmony_ci { ATAPI_MODE_SENSE, 0, 0x2a, 0, 36, 0, 0, 0, 0, 0, 0, 0 }; 5368c2ecf20Sopenharmony_ci char ls_cmd[12] = 5378c2ecf20Sopenharmony_ci { ATAPI_LOG_SENSE, 0, 0x71, 0, 0, 0, 0, 0, 36, 0, 0, 0 }; 5388c2ecf20Sopenharmony_ci char buf[36]; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci s = pt_atapi(tape, id_cmd, 36, buf, "identify"); 5418c2ecf20Sopenharmony_ci if (s) 5428c2ecf20Sopenharmony_ci return -1; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci dt = buf[0] & 0x1f; 5458c2ecf20Sopenharmony_ci if (dt != 1) { 5468c2ecf20Sopenharmony_ci if (verbose) 5478c2ecf20Sopenharmony_ci printk("%s: Drive %d, unsupported type %d\n", 5488c2ecf20Sopenharmony_ci tape->name, tape->drive, dt); 5498c2ecf20Sopenharmony_ci return -1; 5508c2ecf20Sopenharmony_ci } 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci xs(buf, mf, 8, 8); 5538c2ecf20Sopenharmony_ci xs(buf, id, 16, 16); 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci tape->flags = 0; 5568c2ecf20Sopenharmony_ci tape->capacity = 0; 5578c2ecf20Sopenharmony_ci tape->bs = 0; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci if (!pt_ready_wait(tape, PT_READY_TMO)) 5608c2ecf20Sopenharmony_ci tape->flags |= PT_MEDIA; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci if (!pt_atapi(tape, ms_cmd, 36, buf, "mode sense")) { 5638c2ecf20Sopenharmony_ci if (!(buf[2] & 0x80)) 5648c2ecf20Sopenharmony_ci tape->flags |= PT_WRITE_OK; 5658c2ecf20Sopenharmony_ci tape->bs = xn(buf, 10, 2); 5668c2ecf20Sopenharmony_ci } 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci if (!pt_atapi(tape, ls_cmd, 36, buf, "log sense")) 5698c2ecf20Sopenharmony_ci tape->capacity = xn(buf, 24, 4); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci printk("%s: %s %s, %s", tape->name, mf, id, ms[tape->drive]); 5728c2ecf20Sopenharmony_ci if (!(tape->flags & PT_MEDIA)) 5738c2ecf20Sopenharmony_ci printk(", no media\n"); 5748c2ecf20Sopenharmony_ci else { 5758c2ecf20Sopenharmony_ci if (!(tape->flags & PT_WRITE_OK)) 5768c2ecf20Sopenharmony_ci printk(", RO"); 5778c2ecf20Sopenharmony_ci printk(", blocksize %d, %d MB\n", tape->bs, tape->capacity / 1024); 5788c2ecf20Sopenharmony_ci } 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci return 0; 5818c2ecf20Sopenharmony_ci} 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci/* 5858c2ecf20Sopenharmony_ci * returns 0, with id set if drive is detected 5868c2ecf20Sopenharmony_ci * -1, if drive detection failed 5878c2ecf20Sopenharmony_ci */ 5888c2ecf20Sopenharmony_cistatic int pt_probe(struct pt_unit *tape) 5898c2ecf20Sopenharmony_ci{ 5908c2ecf20Sopenharmony_ci if (tape->drive == -1) { 5918c2ecf20Sopenharmony_ci for (tape->drive = 0; tape->drive <= 1; tape->drive++) 5928c2ecf20Sopenharmony_ci if (!pt_reset(tape)) 5938c2ecf20Sopenharmony_ci return pt_identify(tape); 5948c2ecf20Sopenharmony_ci } else { 5958c2ecf20Sopenharmony_ci if (!pt_reset(tape)) 5968c2ecf20Sopenharmony_ci return pt_identify(tape); 5978c2ecf20Sopenharmony_ci } 5988c2ecf20Sopenharmony_ci return -1; 5998c2ecf20Sopenharmony_ci} 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_cistatic int pt_detect(void) 6028c2ecf20Sopenharmony_ci{ 6038c2ecf20Sopenharmony_ci struct pt_unit *tape; 6048c2ecf20Sopenharmony_ci int specified = 0, found = 0; 6058c2ecf20Sopenharmony_ci int unit; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci printk("%s: %s version %s, major %d\n", name, name, PT_VERSION, major); 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci par_drv = pi_register_driver(name); 6108c2ecf20Sopenharmony_ci if (!par_drv) { 6118c2ecf20Sopenharmony_ci pr_err("failed to register %s driver\n", name); 6128c2ecf20Sopenharmony_ci return -1; 6138c2ecf20Sopenharmony_ci } 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci specified = 0; 6168c2ecf20Sopenharmony_ci for (unit = 0; unit < PT_UNITS; unit++) { 6178c2ecf20Sopenharmony_ci struct pt_unit *tape = &pt[unit]; 6188c2ecf20Sopenharmony_ci tape->pi = &tape->pia; 6198c2ecf20Sopenharmony_ci atomic_set(&tape->available, 1); 6208c2ecf20Sopenharmony_ci tape->flags = 0; 6218c2ecf20Sopenharmony_ci tape->last_sense = 0; 6228c2ecf20Sopenharmony_ci tape->present = 0; 6238c2ecf20Sopenharmony_ci tape->bufptr = NULL; 6248c2ecf20Sopenharmony_ci tape->drive = DU[D_SLV]; 6258c2ecf20Sopenharmony_ci snprintf(tape->name, PT_NAMELEN, "%s%d", name, unit); 6268c2ecf20Sopenharmony_ci if (!DU[D_PRT]) 6278c2ecf20Sopenharmony_ci continue; 6288c2ecf20Sopenharmony_ci specified++; 6298c2ecf20Sopenharmony_ci if (pi_init(tape->pi, 0, DU[D_PRT], DU[D_MOD], DU[D_UNI], 6308c2ecf20Sopenharmony_ci DU[D_PRO], DU[D_DLY], pt_scratch, PI_PT, 6318c2ecf20Sopenharmony_ci verbose, tape->name)) { 6328c2ecf20Sopenharmony_ci if (!pt_probe(tape)) { 6338c2ecf20Sopenharmony_ci tape->present = 1; 6348c2ecf20Sopenharmony_ci found++; 6358c2ecf20Sopenharmony_ci } else 6368c2ecf20Sopenharmony_ci pi_release(tape->pi); 6378c2ecf20Sopenharmony_ci } 6388c2ecf20Sopenharmony_ci } 6398c2ecf20Sopenharmony_ci if (specified == 0) { 6408c2ecf20Sopenharmony_ci tape = pt; 6418c2ecf20Sopenharmony_ci if (pi_init(tape->pi, 1, -1, -1, -1, -1, -1, pt_scratch, 6428c2ecf20Sopenharmony_ci PI_PT, verbose, tape->name)) { 6438c2ecf20Sopenharmony_ci if (!pt_probe(tape)) { 6448c2ecf20Sopenharmony_ci tape->present = 1; 6458c2ecf20Sopenharmony_ci found++; 6468c2ecf20Sopenharmony_ci } else 6478c2ecf20Sopenharmony_ci pi_release(tape->pi); 6488c2ecf20Sopenharmony_ci } 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci } 6518c2ecf20Sopenharmony_ci if (found) 6528c2ecf20Sopenharmony_ci return 0; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci pi_unregister_driver(par_drv); 6558c2ecf20Sopenharmony_ci printk("%s: No ATAPI tape drive detected\n", name); 6568c2ecf20Sopenharmony_ci return -1; 6578c2ecf20Sopenharmony_ci} 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_cistatic int pt_open(struct inode *inode, struct file *file) 6608c2ecf20Sopenharmony_ci{ 6618c2ecf20Sopenharmony_ci int unit = iminor(inode) & 0x7F; 6628c2ecf20Sopenharmony_ci struct pt_unit *tape = pt + unit; 6638c2ecf20Sopenharmony_ci int err; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci mutex_lock(&pt_mutex); 6668c2ecf20Sopenharmony_ci if (unit >= PT_UNITS || (!tape->present)) { 6678c2ecf20Sopenharmony_ci mutex_unlock(&pt_mutex); 6688c2ecf20Sopenharmony_ci return -ENODEV; 6698c2ecf20Sopenharmony_ci } 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci err = -EBUSY; 6728c2ecf20Sopenharmony_ci if (!atomic_dec_and_test(&tape->available)) 6738c2ecf20Sopenharmony_ci goto out; 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci pt_identify(tape); 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci err = -ENODEV; 6788c2ecf20Sopenharmony_ci if (!(tape->flags & PT_MEDIA)) 6798c2ecf20Sopenharmony_ci goto out; 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci err = -EROFS; 6828c2ecf20Sopenharmony_ci if ((!(tape->flags & PT_WRITE_OK)) && (file->f_mode & FMODE_WRITE)) 6838c2ecf20Sopenharmony_ci goto out; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci if (!(iminor(inode) & 128)) 6868c2ecf20Sopenharmony_ci tape->flags |= PT_REWIND; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci err = -ENOMEM; 6898c2ecf20Sopenharmony_ci tape->bufptr = kmalloc(PT_BUFSIZE, GFP_KERNEL); 6908c2ecf20Sopenharmony_ci if (tape->bufptr == NULL) { 6918c2ecf20Sopenharmony_ci printk("%s: buffer allocation failed\n", tape->name); 6928c2ecf20Sopenharmony_ci goto out; 6938c2ecf20Sopenharmony_ci } 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci file->private_data = tape; 6968c2ecf20Sopenharmony_ci mutex_unlock(&pt_mutex); 6978c2ecf20Sopenharmony_ci return 0; 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ciout: 7008c2ecf20Sopenharmony_ci atomic_inc(&tape->available); 7018c2ecf20Sopenharmony_ci mutex_unlock(&pt_mutex); 7028c2ecf20Sopenharmony_ci return err; 7038c2ecf20Sopenharmony_ci} 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_cistatic long pt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 7068c2ecf20Sopenharmony_ci{ 7078c2ecf20Sopenharmony_ci struct pt_unit *tape = file->private_data; 7088c2ecf20Sopenharmony_ci struct mtop __user *p = (void __user *)arg; 7098c2ecf20Sopenharmony_ci struct mtop mtop; 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci switch (cmd) { 7128c2ecf20Sopenharmony_ci case MTIOCTOP: 7138c2ecf20Sopenharmony_ci if (copy_from_user(&mtop, p, sizeof(struct mtop))) 7148c2ecf20Sopenharmony_ci return -EFAULT; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci switch (mtop.mt_op) { 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci case MTREW: 7198c2ecf20Sopenharmony_ci mutex_lock(&pt_mutex); 7208c2ecf20Sopenharmony_ci pt_rewind(tape); 7218c2ecf20Sopenharmony_ci mutex_unlock(&pt_mutex); 7228c2ecf20Sopenharmony_ci return 0; 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci case MTWEOF: 7258c2ecf20Sopenharmony_ci mutex_lock(&pt_mutex); 7268c2ecf20Sopenharmony_ci pt_write_fm(tape); 7278c2ecf20Sopenharmony_ci mutex_unlock(&pt_mutex); 7288c2ecf20Sopenharmony_ci return 0; 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci default: 7318c2ecf20Sopenharmony_ci /* FIXME: rate limit ?? */ 7328c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: Unimplemented mt_op %d\n", tape->name, 7338c2ecf20Sopenharmony_ci mtop.mt_op); 7348c2ecf20Sopenharmony_ci return -EINVAL; 7358c2ecf20Sopenharmony_ci } 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci default: 7388c2ecf20Sopenharmony_ci return -ENOTTY; 7398c2ecf20Sopenharmony_ci } 7408c2ecf20Sopenharmony_ci} 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_cistatic int 7438c2ecf20Sopenharmony_cipt_release(struct inode *inode, struct file *file) 7448c2ecf20Sopenharmony_ci{ 7458c2ecf20Sopenharmony_ci struct pt_unit *tape = file->private_data; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci if (atomic_read(&tape->available) > 1) 7488c2ecf20Sopenharmony_ci return -EINVAL; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci if (tape->flags & PT_WRITING) 7518c2ecf20Sopenharmony_ci pt_write_fm(tape); 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci if (tape->flags & PT_REWIND) 7548c2ecf20Sopenharmony_ci pt_rewind(tape); 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci kfree(tape->bufptr); 7578c2ecf20Sopenharmony_ci tape->bufptr = NULL; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci atomic_inc(&tape->available); 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci return 0; 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci} 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_cistatic ssize_t pt_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos) 7668c2ecf20Sopenharmony_ci{ 7678c2ecf20Sopenharmony_ci struct pt_unit *tape = filp->private_data; 7688c2ecf20Sopenharmony_ci struct pi_adapter *pi = tape->pi; 7698c2ecf20Sopenharmony_ci char rd_cmd[12] = { ATAPI_READ_6, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; 7708c2ecf20Sopenharmony_ci int k, n, r, p, s, t, b; 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci if (!(tape->flags & (PT_READING | PT_WRITING))) { 7738c2ecf20Sopenharmony_ci tape->flags |= PT_READING; 7748c2ecf20Sopenharmony_ci if (pt_atapi(tape, rd_cmd, 0, NULL, "start read-ahead")) 7758c2ecf20Sopenharmony_ci return -EIO; 7768c2ecf20Sopenharmony_ci } else if (tape->flags & PT_WRITING) 7778c2ecf20Sopenharmony_ci return -EIO; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci if (tape->flags & PT_EOF) 7808c2ecf20Sopenharmony_ci return 0; 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci t = 0; 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci while (count > 0) { 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci if (!pt_poll_dsc(tape, HZ / 100, PT_TMO, "read")) 7878c2ecf20Sopenharmony_ci return -EIO; 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci n = count; 7908c2ecf20Sopenharmony_ci if (n > 32768) 7918c2ecf20Sopenharmony_ci n = 32768; /* max per command */ 7928c2ecf20Sopenharmony_ci b = (n - 1 + tape->bs) / tape->bs; 7938c2ecf20Sopenharmony_ci n = b * tape->bs; /* rounded up to even block */ 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci rd_cmd[4] = b; 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci r = pt_command(tape, rd_cmd, n, "read"); 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci mdelay(1); 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci if (r) { 8028c2ecf20Sopenharmony_ci pt_req_sense(tape, 0); 8038c2ecf20Sopenharmony_ci return -EIO; 8048c2ecf20Sopenharmony_ci } 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci while (1) { 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci r = pt_wait(tape, STAT_BUSY, 8098c2ecf20Sopenharmony_ci STAT_DRQ | STAT_ERR | STAT_READY, 8108c2ecf20Sopenharmony_ci DBMSG("read DRQ"), ""); 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci if (r & STAT_SENSE) { 8138c2ecf20Sopenharmony_ci pi_disconnect(pi); 8148c2ecf20Sopenharmony_ci pt_req_sense(tape, 0); 8158c2ecf20Sopenharmony_ci return -EIO; 8168c2ecf20Sopenharmony_ci } 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci if (r) 8198c2ecf20Sopenharmony_ci tape->flags |= PT_EOF; 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci s = read_reg(pi, 7); 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci if (!(s & STAT_DRQ)) 8248c2ecf20Sopenharmony_ci break; 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci n = (read_reg(pi, 4) + 256 * read_reg(pi, 5)); 8278c2ecf20Sopenharmony_ci p = (read_reg(pi, 2) & 3); 8288c2ecf20Sopenharmony_ci if (p != 2) { 8298c2ecf20Sopenharmony_ci pi_disconnect(pi); 8308c2ecf20Sopenharmony_ci printk("%s: Phase error on read: %d\n", tape->name, 8318c2ecf20Sopenharmony_ci p); 8328c2ecf20Sopenharmony_ci return -EIO; 8338c2ecf20Sopenharmony_ci } 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci while (n > 0) { 8368c2ecf20Sopenharmony_ci k = n; 8378c2ecf20Sopenharmony_ci if (k > PT_BUFSIZE) 8388c2ecf20Sopenharmony_ci k = PT_BUFSIZE; 8398c2ecf20Sopenharmony_ci pi_read_block(pi, tape->bufptr, k); 8408c2ecf20Sopenharmony_ci n -= k; 8418c2ecf20Sopenharmony_ci b = k; 8428c2ecf20Sopenharmony_ci if (b > count) 8438c2ecf20Sopenharmony_ci b = count; 8448c2ecf20Sopenharmony_ci if (copy_to_user(buf + t, tape->bufptr, b)) { 8458c2ecf20Sopenharmony_ci pi_disconnect(pi); 8468c2ecf20Sopenharmony_ci return -EFAULT; 8478c2ecf20Sopenharmony_ci } 8488c2ecf20Sopenharmony_ci t += b; 8498c2ecf20Sopenharmony_ci count -= b; 8508c2ecf20Sopenharmony_ci } 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci } 8538c2ecf20Sopenharmony_ci pi_disconnect(pi); 8548c2ecf20Sopenharmony_ci if (tape->flags & PT_EOF) 8558c2ecf20Sopenharmony_ci break; 8568c2ecf20Sopenharmony_ci } 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci return t; 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci} 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_cistatic ssize_t pt_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) 8638c2ecf20Sopenharmony_ci{ 8648c2ecf20Sopenharmony_ci struct pt_unit *tape = filp->private_data; 8658c2ecf20Sopenharmony_ci struct pi_adapter *pi = tape->pi; 8668c2ecf20Sopenharmony_ci char wr_cmd[12] = { ATAPI_WRITE_6, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; 8678c2ecf20Sopenharmony_ci int k, n, r, p, s, t, b; 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci if (!(tape->flags & PT_WRITE_OK)) 8708c2ecf20Sopenharmony_ci return -EROFS; 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci if (!(tape->flags & (PT_READING | PT_WRITING))) { 8738c2ecf20Sopenharmony_ci tape->flags |= PT_WRITING; 8748c2ecf20Sopenharmony_ci if (pt_atapi 8758c2ecf20Sopenharmony_ci (tape, wr_cmd, 0, NULL, "start buffer-available mode")) 8768c2ecf20Sopenharmony_ci return -EIO; 8778c2ecf20Sopenharmony_ci } else if (tape->flags & PT_READING) 8788c2ecf20Sopenharmony_ci return -EIO; 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci if (tape->flags & PT_EOF) 8818c2ecf20Sopenharmony_ci return -ENOSPC; 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci t = 0; 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci while (count > 0) { 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci if (!pt_poll_dsc(tape, HZ / 100, PT_TMO, "write")) 8888c2ecf20Sopenharmony_ci return -EIO; 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci n = count; 8918c2ecf20Sopenharmony_ci if (n > 32768) 8928c2ecf20Sopenharmony_ci n = 32768; /* max per command */ 8938c2ecf20Sopenharmony_ci b = (n - 1 + tape->bs) / tape->bs; 8948c2ecf20Sopenharmony_ci n = b * tape->bs; /* rounded up to even block */ 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci wr_cmd[4] = b; 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci r = pt_command(tape, wr_cmd, n, "write"); 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci mdelay(1); 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci if (r) { /* error delivering command only */ 9038c2ecf20Sopenharmony_ci pt_req_sense(tape, 0); 9048c2ecf20Sopenharmony_ci return -EIO; 9058c2ecf20Sopenharmony_ci } 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci while (1) { 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci r = pt_wait(tape, STAT_BUSY, 9108c2ecf20Sopenharmony_ci STAT_DRQ | STAT_ERR | STAT_READY, 9118c2ecf20Sopenharmony_ci DBMSG("write DRQ"), NULL); 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci if (r & STAT_SENSE) { 9148c2ecf20Sopenharmony_ci pi_disconnect(pi); 9158c2ecf20Sopenharmony_ci pt_req_sense(tape, 0); 9168c2ecf20Sopenharmony_ci return -EIO; 9178c2ecf20Sopenharmony_ci } 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci if (r) 9208c2ecf20Sopenharmony_ci tape->flags |= PT_EOF; 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci s = read_reg(pi, 7); 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci if (!(s & STAT_DRQ)) 9258c2ecf20Sopenharmony_ci break; 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci n = (read_reg(pi, 4) + 256 * read_reg(pi, 5)); 9288c2ecf20Sopenharmony_ci p = (read_reg(pi, 2) & 3); 9298c2ecf20Sopenharmony_ci if (p != 0) { 9308c2ecf20Sopenharmony_ci pi_disconnect(pi); 9318c2ecf20Sopenharmony_ci printk("%s: Phase error on write: %d \n", 9328c2ecf20Sopenharmony_ci tape->name, p); 9338c2ecf20Sopenharmony_ci return -EIO; 9348c2ecf20Sopenharmony_ci } 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci while (n > 0) { 9378c2ecf20Sopenharmony_ci k = n; 9388c2ecf20Sopenharmony_ci if (k > PT_BUFSIZE) 9398c2ecf20Sopenharmony_ci k = PT_BUFSIZE; 9408c2ecf20Sopenharmony_ci b = k; 9418c2ecf20Sopenharmony_ci if (b > count) 9428c2ecf20Sopenharmony_ci b = count; 9438c2ecf20Sopenharmony_ci if (copy_from_user(tape->bufptr, buf + t, b)) { 9448c2ecf20Sopenharmony_ci pi_disconnect(pi); 9458c2ecf20Sopenharmony_ci return -EFAULT; 9468c2ecf20Sopenharmony_ci } 9478c2ecf20Sopenharmony_ci pi_write_block(pi, tape->bufptr, k); 9488c2ecf20Sopenharmony_ci t += b; 9498c2ecf20Sopenharmony_ci count -= b; 9508c2ecf20Sopenharmony_ci n -= k; 9518c2ecf20Sopenharmony_ci } 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci } 9548c2ecf20Sopenharmony_ci pi_disconnect(pi); 9558c2ecf20Sopenharmony_ci if (tape->flags & PT_EOF) 9568c2ecf20Sopenharmony_ci break; 9578c2ecf20Sopenharmony_ci } 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci return t; 9608c2ecf20Sopenharmony_ci} 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_cistatic int __init pt_init(void) 9638c2ecf20Sopenharmony_ci{ 9648c2ecf20Sopenharmony_ci int unit; 9658c2ecf20Sopenharmony_ci int err; 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci if (disable) { 9688c2ecf20Sopenharmony_ci err = -EINVAL; 9698c2ecf20Sopenharmony_ci goto out; 9708c2ecf20Sopenharmony_ci } 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci if (pt_detect()) { 9738c2ecf20Sopenharmony_ci err = -ENODEV; 9748c2ecf20Sopenharmony_ci goto out; 9758c2ecf20Sopenharmony_ci } 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci err = register_chrdev(major, name, &pt_fops); 9788c2ecf20Sopenharmony_ci if (err < 0) { 9798c2ecf20Sopenharmony_ci printk("pt_init: unable to get major number %d\n", major); 9808c2ecf20Sopenharmony_ci for (unit = 0; unit < PT_UNITS; unit++) 9818c2ecf20Sopenharmony_ci if (pt[unit].present) 9828c2ecf20Sopenharmony_ci pi_release(pt[unit].pi); 9838c2ecf20Sopenharmony_ci goto out; 9848c2ecf20Sopenharmony_ci } 9858c2ecf20Sopenharmony_ci major = err; 9868c2ecf20Sopenharmony_ci pt_class = class_create(THIS_MODULE, "pt"); 9878c2ecf20Sopenharmony_ci if (IS_ERR(pt_class)) { 9888c2ecf20Sopenharmony_ci err = PTR_ERR(pt_class); 9898c2ecf20Sopenharmony_ci goto out_chrdev; 9908c2ecf20Sopenharmony_ci } 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci for (unit = 0; unit < PT_UNITS; unit++) 9938c2ecf20Sopenharmony_ci if (pt[unit].present) { 9948c2ecf20Sopenharmony_ci device_create(pt_class, NULL, MKDEV(major, unit), NULL, 9958c2ecf20Sopenharmony_ci "pt%d", unit); 9968c2ecf20Sopenharmony_ci device_create(pt_class, NULL, MKDEV(major, unit + 128), 9978c2ecf20Sopenharmony_ci NULL, "pt%dn", unit); 9988c2ecf20Sopenharmony_ci } 9998c2ecf20Sopenharmony_ci goto out; 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ciout_chrdev: 10028c2ecf20Sopenharmony_ci unregister_chrdev(major, "pt"); 10038c2ecf20Sopenharmony_ciout: 10048c2ecf20Sopenharmony_ci return err; 10058c2ecf20Sopenharmony_ci} 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_cistatic void __exit pt_exit(void) 10088c2ecf20Sopenharmony_ci{ 10098c2ecf20Sopenharmony_ci int unit; 10108c2ecf20Sopenharmony_ci for (unit = 0; unit < PT_UNITS; unit++) 10118c2ecf20Sopenharmony_ci if (pt[unit].present) { 10128c2ecf20Sopenharmony_ci device_destroy(pt_class, MKDEV(major, unit)); 10138c2ecf20Sopenharmony_ci device_destroy(pt_class, MKDEV(major, unit + 128)); 10148c2ecf20Sopenharmony_ci } 10158c2ecf20Sopenharmony_ci class_destroy(pt_class); 10168c2ecf20Sopenharmony_ci unregister_chrdev(major, name); 10178c2ecf20Sopenharmony_ci for (unit = 0; unit < PT_UNITS; unit++) 10188c2ecf20Sopenharmony_ci if (pt[unit].present) 10198c2ecf20Sopenharmony_ci pi_release(pt[unit].pi); 10208c2ecf20Sopenharmony_ci} 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 10238c2ecf20Sopenharmony_cimodule_init(pt_init) 10248c2ecf20Sopenharmony_cimodule_exit(pt_exit) 1025