18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci pg.c (c) 1998 Grant R. Guenther <grant@torque.net> 38c2ecf20Sopenharmony_ci Under the terms of the GNU General Public License. 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci The pg driver provides a simple character device interface for 68c2ecf20Sopenharmony_ci sending ATAPI commands to a device. With the exception of the 78c2ecf20Sopenharmony_ci ATAPI reset operation, all operations are performed by a pair 88c2ecf20Sopenharmony_ci of read and write operations to the appropriate /dev/pgN device. 98c2ecf20Sopenharmony_ci A write operation delivers a command and any outbound data in 108c2ecf20Sopenharmony_ci a single buffer. Normally, the write will succeed unless the 118c2ecf20Sopenharmony_ci device is offline or malfunctioning, or there is already another 128c2ecf20Sopenharmony_ci command pending. If the write succeeds, it should be followed 138c2ecf20Sopenharmony_ci immediately by a read operation, to obtain any returned data and 148c2ecf20Sopenharmony_ci status information. A read will fail if there is no operation 158c2ecf20Sopenharmony_ci in progress. 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci As a special case, the device can be reset with a write operation, 188c2ecf20Sopenharmony_ci and in this case, no following read is expected, or permitted. 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci There are no ioctl() operations. Any single operation 218c2ecf20Sopenharmony_ci may transfer at most PG_MAX_DATA bytes. Note that the driver must 228c2ecf20Sopenharmony_ci copy the data through an internal buffer. In keeping with all 238c2ecf20Sopenharmony_ci current ATAPI devices, command packets are assumed to be exactly 248c2ecf20Sopenharmony_ci 12 bytes in length. 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci To permit future changes to this interface, the headers in the 278c2ecf20Sopenharmony_ci read and write buffers contain a single character "magic" flag. 288c2ecf20Sopenharmony_ci Currently this flag must be the character "P". 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci By default, the driver will autoprobe for a single parallel 318c2ecf20Sopenharmony_ci port ATAPI device, but if their individual parameters are 328c2ecf20Sopenharmony_ci specified, the driver can handle up to 4 devices. 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci To use this device, you must have the following device 358c2ecf20Sopenharmony_ci special files defined: 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci /dev/pg0 c 97 0 388c2ecf20Sopenharmony_ci /dev/pg1 c 97 1 398c2ecf20Sopenharmony_ci /dev/pg2 c 97 2 408c2ecf20Sopenharmony_ci /dev/pg3 c 97 3 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci (You'll need to change the 97 to something else if you use 438c2ecf20Sopenharmony_ci the 'major' parameter to install the driver on a different 448c2ecf20Sopenharmony_ci major number.) 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci The behaviour of the pg driver can be altered by setting 478c2ecf20Sopenharmony_ci some parameters from the insmod command line. The following 488c2ecf20Sopenharmony_ci parameters are adjustable: 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci drive0 These four arguments can be arrays of 518c2ecf20Sopenharmony_ci drive1 1-6 integers as follows: 528c2ecf20Sopenharmony_ci drive2 538c2ecf20Sopenharmony_ci drive3 <prt>,<pro>,<uni>,<mod>,<slv>,<dly> 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci Where, 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci <prt> is the base of the parallel port address for 588c2ecf20Sopenharmony_ci the corresponding drive. (required) 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci <pro> is the protocol number for the adapter that 618c2ecf20Sopenharmony_ci supports this drive. These numbers are 628c2ecf20Sopenharmony_ci logged by 'paride' when the protocol modules 638c2ecf20Sopenharmony_ci are initialised. (0 if not given) 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci <uni> for those adapters that support chained 668c2ecf20Sopenharmony_ci devices, this is the unit selector for the 678c2ecf20Sopenharmony_ci chain of devices on the given port. It should 688c2ecf20Sopenharmony_ci be zero for devices that don't support chaining. 698c2ecf20Sopenharmony_ci (0 if not given) 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci <mod> this can be -1 to choose the best mode, or one 728c2ecf20Sopenharmony_ci of the mode numbers supported by the adapter. 738c2ecf20Sopenharmony_ci (-1 if not given) 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci <slv> ATAPI devices can be jumpered to master or slave. 768c2ecf20Sopenharmony_ci Set this to 0 to choose the master drive, 1 to 778c2ecf20Sopenharmony_ci choose the slave, -1 (the default) to choose the 788c2ecf20Sopenharmony_ci first drive found. 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci <dly> some parallel ports require the driver to 818c2ecf20Sopenharmony_ci go more slowly. -1 sets a default value that 828c2ecf20Sopenharmony_ci should work with the chosen protocol. Otherwise, 838c2ecf20Sopenharmony_ci set this to a small integer, the larger it is 848c2ecf20Sopenharmony_ci the slower the port i/o. In some cases, setting 858c2ecf20Sopenharmony_ci this to zero will speed up the device. (default -1) 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci major You may use this parameter to override the 888c2ecf20Sopenharmony_ci default major number (97) that this driver 898c2ecf20Sopenharmony_ci will use. Be sure to change the device 908c2ecf20Sopenharmony_ci name as well. 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci name This parameter is a character string that 938c2ecf20Sopenharmony_ci contains the name the kernel will use for this 948c2ecf20Sopenharmony_ci device (in /proc output, for instance). 958c2ecf20Sopenharmony_ci (default "pg"). 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci verbose This parameter controls the amount of logging 988c2ecf20Sopenharmony_ci that is done by the driver. Set it to 0 for 998c2ecf20Sopenharmony_ci quiet operation, to 1 to enable progress 1008c2ecf20Sopenharmony_ci messages while the driver probes for devices, 1018c2ecf20Sopenharmony_ci or to 2 for full debug logging. (default 0) 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci If this driver is built into the kernel, you can use 1048c2ecf20Sopenharmony_ci the following command line parameters, with the same values 1058c2ecf20Sopenharmony_ci as the corresponding module parameters listed above: 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci pg.drive0 1088c2ecf20Sopenharmony_ci pg.drive1 1098c2ecf20Sopenharmony_ci pg.drive2 1108c2ecf20Sopenharmony_ci pg.drive3 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci In addition, you can use the parameter pg.disable to disable 1138c2ecf20Sopenharmony_ci the driver entirely. 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci*/ 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci/* Changes: 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci 1.01 GRG 1998.06.16 Bug fixes 1208c2ecf20Sopenharmony_ci 1.02 GRG 1998.09.24 Added jumbo support 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci*/ 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci#define PG_VERSION "1.02" 1258c2ecf20Sopenharmony_ci#define PG_MAJOR 97 1268c2ecf20Sopenharmony_ci#define PG_NAME "pg" 1278c2ecf20Sopenharmony_ci#define PG_UNITS 4 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci#ifndef PI_PG 1308c2ecf20Sopenharmony_ci#define PI_PG 4 1318c2ecf20Sopenharmony_ci#endif 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci#include <linux/types.h> 1348c2ecf20Sopenharmony_ci/* Here are things one can override from the insmod command. 1358c2ecf20Sopenharmony_ci Most are autoprobed by paride unless set here. Verbose is 0 1368c2ecf20Sopenharmony_ci by default. 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci*/ 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic int verbose; 1418c2ecf20Sopenharmony_cistatic int major = PG_MAJOR; 1428c2ecf20Sopenharmony_cistatic char *name = PG_NAME; 1438c2ecf20Sopenharmony_cistatic int disable = 0; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic int drive0[6] = { 0, 0, 0, -1, -1, -1 }; 1468c2ecf20Sopenharmony_cistatic int drive1[6] = { 0, 0, 0, -1, -1, -1 }; 1478c2ecf20Sopenharmony_cistatic int drive2[6] = { 0, 0, 0, -1, -1, -1 }; 1488c2ecf20Sopenharmony_cistatic int drive3[6] = { 0, 0, 0, -1, -1, -1 }; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic int (*drives[4])[6] = {&drive0, &drive1, &drive2, &drive3}; 1518c2ecf20Sopenharmony_cistatic int pg_drive_count; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cienum {D_PRT, D_PRO, D_UNI, D_MOD, D_SLV, D_DLY}; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci/* end of parameters */ 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci#include <linux/module.h> 1588c2ecf20Sopenharmony_ci#include <linux/init.h> 1598c2ecf20Sopenharmony_ci#include <linux/fs.h> 1608c2ecf20Sopenharmony_ci#include <linux/delay.h> 1618c2ecf20Sopenharmony_ci#include <linux/slab.h> 1628c2ecf20Sopenharmony_ci#include <linux/mtio.h> 1638c2ecf20Sopenharmony_ci#include <linux/pg.h> 1648c2ecf20Sopenharmony_ci#include <linux/device.h> 1658c2ecf20Sopenharmony_ci#include <linux/sched.h> /* current, TASK_* */ 1668c2ecf20Sopenharmony_ci#include <linux/mutex.h> 1678c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cimodule_param(verbose, int, 0644); 1728c2ecf20Sopenharmony_cimodule_param(major, int, 0); 1738c2ecf20Sopenharmony_cimodule_param(name, charp, 0); 1748c2ecf20Sopenharmony_cimodule_param_array(drive0, int, NULL, 0); 1758c2ecf20Sopenharmony_cimodule_param_array(drive1, int, NULL, 0); 1768c2ecf20Sopenharmony_cimodule_param_array(drive2, int, NULL, 0); 1778c2ecf20Sopenharmony_cimodule_param_array(drive3, int, NULL, 0); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci#include "paride.h" 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci#define PG_SPIN_DEL 50 /* spin delay in micro-seconds */ 1828c2ecf20Sopenharmony_ci#define PG_SPIN 200 1838c2ecf20Sopenharmony_ci#define PG_TMO HZ 1848c2ecf20Sopenharmony_ci#define PG_RESET_TMO 10*HZ 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci#define STAT_ERR 0x01 1878c2ecf20Sopenharmony_ci#define STAT_INDEX 0x02 1888c2ecf20Sopenharmony_ci#define STAT_ECC 0x04 1898c2ecf20Sopenharmony_ci#define STAT_DRQ 0x08 1908c2ecf20Sopenharmony_ci#define STAT_SEEK 0x10 1918c2ecf20Sopenharmony_ci#define STAT_WRERR 0x20 1928c2ecf20Sopenharmony_ci#define STAT_READY 0x40 1938c2ecf20Sopenharmony_ci#define STAT_BUSY 0x80 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci#define ATAPI_IDENTIFY 0x12 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(pg_mutex); 1988c2ecf20Sopenharmony_cistatic int pg_open(struct inode *inode, struct file *file); 1998c2ecf20Sopenharmony_cistatic int pg_release(struct inode *inode, struct file *file); 2008c2ecf20Sopenharmony_cistatic ssize_t pg_read(struct file *filp, char __user *buf, 2018c2ecf20Sopenharmony_ci size_t count, loff_t * ppos); 2028c2ecf20Sopenharmony_cistatic ssize_t pg_write(struct file *filp, const char __user *buf, 2038c2ecf20Sopenharmony_ci size_t count, loff_t * ppos); 2048c2ecf20Sopenharmony_cistatic int pg_detect(void); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci#define PG_NAMELEN 8 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistruct pg { 2098c2ecf20Sopenharmony_ci struct pi_adapter pia; /* interface to paride layer */ 2108c2ecf20Sopenharmony_ci struct pi_adapter *pi; 2118c2ecf20Sopenharmony_ci int busy; /* write done, read expected */ 2128c2ecf20Sopenharmony_ci int start; /* jiffies at command start */ 2138c2ecf20Sopenharmony_ci int dlen; /* transfer size requested */ 2148c2ecf20Sopenharmony_ci unsigned long timeout; /* timeout requested */ 2158c2ecf20Sopenharmony_ci int status; /* last sense key */ 2168c2ecf20Sopenharmony_ci int drive; /* drive */ 2178c2ecf20Sopenharmony_ci unsigned long access; /* count of active opens ... */ 2188c2ecf20Sopenharmony_ci int present; /* device present ? */ 2198c2ecf20Sopenharmony_ci char *bufptr; 2208c2ecf20Sopenharmony_ci char name[PG_NAMELEN]; /* pg0, pg1, ... */ 2218c2ecf20Sopenharmony_ci}; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_cistatic struct pg devices[PG_UNITS]; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic int pg_identify(struct pg *dev, int log); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic char pg_scratch[512]; /* scratch block buffer */ 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic struct class *pg_class; 2308c2ecf20Sopenharmony_cistatic void *par_drv; /* reference of parport driver */ 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci/* kernel glue structures */ 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistatic const struct file_operations pg_fops = { 2358c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 2368c2ecf20Sopenharmony_ci .read = pg_read, 2378c2ecf20Sopenharmony_ci .write = pg_write, 2388c2ecf20Sopenharmony_ci .open = pg_open, 2398c2ecf20Sopenharmony_ci .release = pg_release, 2408c2ecf20Sopenharmony_ci .llseek = noop_llseek, 2418c2ecf20Sopenharmony_ci}; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_cistatic void pg_init_units(void) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci int unit; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci pg_drive_count = 0; 2488c2ecf20Sopenharmony_ci for (unit = 0; unit < PG_UNITS; unit++) { 2498c2ecf20Sopenharmony_ci int *parm = *drives[unit]; 2508c2ecf20Sopenharmony_ci struct pg *dev = &devices[unit]; 2518c2ecf20Sopenharmony_ci dev->pi = &dev->pia; 2528c2ecf20Sopenharmony_ci clear_bit(0, &dev->access); 2538c2ecf20Sopenharmony_ci dev->busy = 0; 2548c2ecf20Sopenharmony_ci dev->present = 0; 2558c2ecf20Sopenharmony_ci dev->bufptr = NULL; 2568c2ecf20Sopenharmony_ci dev->drive = parm[D_SLV]; 2578c2ecf20Sopenharmony_ci snprintf(dev->name, PG_NAMELEN, "%s%c", name, 'a'+unit); 2588c2ecf20Sopenharmony_ci if (parm[D_PRT]) 2598c2ecf20Sopenharmony_ci pg_drive_count++; 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cistatic inline int status_reg(struct pg *dev) 2648c2ecf20Sopenharmony_ci{ 2658c2ecf20Sopenharmony_ci return pi_read_regr(dev->pi, 1, 6); 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic inline int read_reg(struct pg *dev, int reg) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci return pi_read_regr(dev->pi, 0, reg); 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic inline void write_reg(struct pg *dev, int reg, int val) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci pi_write_regr(dev->pi, 0, reg, val); 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_cistatic inline u8 DRIVE(struct pg *dev) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci return 0xa0+0x10*dev->drive; 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cistatic void pg_sleep(int cs) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci schedule_timeout_interruptible(cs); 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_cistatic int pg_wait(struct pg *dev, int go, int stop, unsigned long tmo, char *msg) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci int j, r, e, s, p, to; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci dev->status = 0; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci j = 0; 2958c2ecf20Sopenharmony_ci while ((((r = status_reg(dev)) & go) || (stop && (!(r & stop)))) 2968c2ecf20Sopenharmony_ci && time_before(jiffies, tmo)) { 2978c2ecf20Sopenharmony_ci if (j++ < PG_SPIN) 2988c2ecf20Sopenharmony_ci udelay(PG_SPIN_DEL); 2998c2ecf20Sopenharmony_ci else 3008c2ecf20Sopenharmony_ci pg_sleep(1); 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci to = time_after_eq(jiffies, tmo); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci if ((r & (STAT_ERR & stop)) || to) { 3068c2ecf20Sopenharmony_ci s = read_reg(dev, 7); 3078c2ecf20Sopenharmony_ci e = read_reg(dev, 1); 3088c2ecf20Sopenharmony_ci p = read_reg(dev, 2); 3098c2ecf20Sopenharmony_ci if (verbose > 1) 3108c2ecf20Sopenharmony_ci printk("%s: %s: stat=0x%x err=0x%x phase=%d%s\n", 3118c2ecf20Sopenharmony_ci dev->name, msg, s, e, p, to ? " timeout" : ""); 3128c2ecf20Sopenharmony_ci if (to) 3138c2ecf20Sopenharmony_ci e |= 0x100; 3148c2ecf20Sopenharmony_ci dev->status = (e >> 4) & 0xff; 3158c2ecf20Sopenharmony_ci return -1; 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci return 0; 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_cistatic int pg_command(struct pg *dev, char *cmd, int dlen, unsigned long tmo) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci int k; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci pi_connect(dev->pi); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci write_reg(dev, 6, DRIVE(dev)); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci if (pg_wait(dev, STAT_BUSY | STAT_DRQ, 0, tmo, "before command")) 3298c2ecf20Sopenharmony_ci goto fail; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci write_reg(dev, 4, dlen % 256); 3328c2ecf20Sopenharmony_ci write_reg(dev, 5, dlen / 256); 3338c2ecf20Sopenharmony_ci write_reg(dev, 7, 0xa0); /* ATAPI packet command */ 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci if (pg_wait(dev, STAT_BUSY, STAT_DRQ, tmo, "command DRQ")) 3368c2ecf20Sopenharmony_ci goto fail; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci if (read_reg(dev, 2) != 1) { 3398c2ecf20Sopenharmony_ci printk("%s: command phase error\n", dev->name); 3408c2ecf20Sopenharmony_ci goto fail; 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci pi_write_block(dev->pi, cmd, 12); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci if (verbose > 1) { 3468c2ecf20Sopenharmony_ci printk("%s: Command sent, dlen=%d packet= ", dev->name, dlen); 3478c2ecf20Sopenharmony_ci for (k = 0; k < 12; k++) 3488c2ecf20Sopenharmony_ci printk("%02x ", cmd[k] & 0xff); 3498c2ecf20Sopenharmony_ci printk("\n"); 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci return 0; 3528c2ecf20Sopenharmony_cifail: 3538c2ecf20Sopenharmony_ci pi_disconnect(dev->pi); 3548c2ecf20Sopenharmony_ci return -1; 3558c2ecf20Sopenharmony_ci} 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_cistatic int pg_completion(struct pg *dev, char *buf, unsigned long tmo) 3588c2ecf20Sopenharmony_ci{ 3598c2ecf20Sopenharmony_ci int r, d, n, p; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci r = pg_wait(dev, STAT_BUSY, STAT_DRQ | STAT_READY | STAT_ERR, 3628c2ecf20Sopenharmony_ci tmo, "completion"); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci dev->dlen = 0; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci while (read_reg(dev, 7) & STAT_DRQ) { 3678c2ecf20Sopenharmony_ci d = (read_reg(dev, 4) + 256 * read_reg(dev, 5)); 3688c2ecf20Sopenharmony_ci n = ((d + 3) & 0xfffc); 3698c2ecf20Sopenharmony_ci p = read_reg(dev, 2) & 3; 3708c2ecf20Sopenharmony_ci if (p == 0) 3718c2ecf20Sopenharmony_ci pi_write_block(dev->pi, buf, n); 3728c2ecf20Sopenharmony_ci if (p == 2) 3738c2ecf20Sopenharmony_ci pi_read_block(dev->pi, buf, n); 3748c2ecf20Sopenharmony_ci if (verbose > 1) 3758c2ecf20Sopenharmony_ci printk("%s: %s %d bytes\n", dev->name, 3768c2ecf20Sopenharmony_ci p ? "Read" : "Write", n); 3778c2ecf20Sopenharmony_ci dev->dlen += (1 - p) * d; 3788c2ecf20Sopenharmony_ci buf += d; 3798c2ecf20Sopenharmony_ci r = pg_wait(dev, STAT_BUSY, STAT_DRQ | STAT_READY | STAT_ERR, 3808c2ecf20Sopenharmony_ci tmo, "completion"); 3818c2ecf20Sopenharmony_ci } 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci pi_disconnect(dev->pi); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci return r; 3868c2ecf20Sopenharmony_ci} 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_cistatic int pg_reset(struct pg *dev) 3898c2ecf20Sopenharmony_ci{ 3908c2ecf20Sopenharmony_ci int i, k, err; 3918c2ecf20Sopenharmony_ci int expect[5] = { 1, 1, 1, 0x14, 0xeb }; 3928c2ecf20Sopenharmony_ci int got[5]; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci pi_connect(dev->pi); 3958c2ecf20Sopenharmony_ci write_reg(dev, 6, DRIVE(dev)); 3968c2ecf20Sopenharmony_ci write_reg(dev, 7, 8); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci pg_sleep(20 * HZ / 1000); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci k = 0; 4018c2ecf20Sopenharmony_ci while ((k++ < PG_RESET_TMO) && (status_reg(dev) & STAT_BUSY)) 4028c2ecf20Sopenharmony_ci pg_sleep(1); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci for (i = 0; i < 5; i++) 4058c2ecf20Sopenharmony_ci got[i] = read_reg(dev, i + 1); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci err = memcmp(expect, got, sizeof(got)) ? -1 : 0; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci if (verbose) { 4108c2ecf20Sopenharmony_ci printk("%s: Reset (%d) signature = ", dev->name, k); 4118c2ecf20Sopenharmony_ci for (i = 0; i < 5; i++) 4128c2ecf20Sopenharmony_ci printk("%3x", got[i]); 4138c2ecf20Sopenharmony_ci if (err) 4148c2ecf20Sopenharmony_ci printk(" (incorrect)"); 4158c2ecf20Sopenharmony_ci printk("\n"); 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci pi_disconnect(dev->pi); 4198c2ecf20Sopenharmony_ci return err; 4208c2ecf20Sopenharmony_ci} 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_cistatic void xs(char *buf, char *targ, int len) 4238c2ecf20Sopenharmony_ci{ 4248c2ecf20Sopenharmony_ci char l = '\0'; 4258c2ecf20Sopenharmony_ci int k; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci for (k = 0; k < len; k++) { 4288c2ecf20Sopenharmony_ci char c = *buf++; 4298c2ecf20Sopenharmony_ci if (c != ' ' && c != l) 4308c2ecf20Sopenharmony_ci l = *targ++ = c; 4318c2ecf20Sopenharmony_ci } 4328c2ecf20Sopenharmony_ci if (l == ' ') 4338c2ecf20Sopenharmony_ci targ--; 4348c2ecf20Sopenharmony_ci *targ = '\0'; 4358c2ecf20Sopenharmony_ci} 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_cistatic int pg_identify(struct pg *dev, int log) 4388c2ecf20Sopenharmony_ci{ 4398c2ecf20Sopenharmony_ci int s; 4408c2ecf20Sopenharmony_ci char *ms[2] = { "master", "slave" }; 4418c2ecf20Sopenharmony_ci char mf[10], id[18]; 4428c2ecf20Sopenharmony_ci char id_cmd[12] = { ATAPI_IDENTIFY, 0, 0, 0, 36, 0, 0, 0, 0, 0, 0, 0 }; 4438c2ecf20Sopenharmony_ci char buf[36]; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci s = pg_command(dev, id_cmd, 36, jiffies + PG_TMO); 4468c2ecf20Sopenharmony_ci if (s) 4478c2ecf20Sopenharmony_ci return -1; 4488c2ecf20Sopenharmony_ci s = pg_completion(dev, buf, jiffies + PG_TMO); 4498c2ecf20Sopenharmony_ci if (s) 4508c2ecf20Sopenharmony_ci return -1; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci if (log) { 4538c2ecf20Sopenharmony_ci xs(buf + 8, mf, 8); 4548c2ecf20Sopenharmony_ci xs(buf + 16, id, 16); 4558c2ecf20Sopenharmony_ci printk("%s: %s %s, %s\n", dev->name, mf, id, ms[dev->drive]); 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci return 0; 4598c2ecf20Sopenharmony_ci} 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci/* 4628c2ecf20Sopenharmony_ci * returns 0, with id set if drive is detected 4638c2ecf20Sopenharmony_ci * -1, if drive detection failed 4648c2ecf20Sopenharmony_ci */ 4658c2ecf20Sopenharmony_cistatic int pg_probe(struct pg *dev) 4668c2ecf20Sopenharmony_ci{ 4678c2ecf20Sopenharmony_ci if (dev->drive == -1) { 4688c2ecf20Sopenharmony_ci for (dev->drive = 0; dev->drive <= 1; dev->drive++) 4698c2ecf20Sopenharmony_ci if (!pg_reset(dev)) 4708c2ecf20Sopenharmony_ci return pg_identify(dev, 1); 4718c2ecf20Sopenharmony_ci } else { 4728c2ecf20Sopenharmony_ci if (!pg_reset(dev)) 4738c2ecf20Sopenharmony_ci return pg_identify(dev, 1); 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci return -1; 4768c2ecf20Sopenharmony_ci} 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_cistatic int pg_detect(void) 4798c2ecf20Sopenharmony_ci{ 4808c2ecf20Sopenharmony_ci struct pg *dev = &devices[0]; 4818c2ecf20Sopenharmony_ci int k, unit; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci printk("%s: %s version %s, major %d\n", name, name, PG_VERSION, major); 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci par_drv = pi_register_driver(name); 4868c2ecf20Sopenharmony_ci if (!par_drv) { 4878c2ecf20Sopenharmony_ci pr_err("failed to register %s driver\n", name); 4888c2ecf20Sopenharmony_ci return -1; 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci k = 0; 4928c2ecf20Sopenharmony_ci if (pg_drive_count == 0) { 4938c2ecf20Sopenharmony_ci if (pi_init(dev->pi, 1, -1, -1, -1, -1, -1, pg_scratch, 4948c2ecf20Sopenharmony_ci PI_PG, verbose, dev->name)) { 4958c2ecf20Sopenharmony_ci if (!pg_probe(dev)) { 4968c2ecf20Sopenharmony_ci dev->present = 1; 4978c2ecf20Sopenharmony_ci k++; 4988c2ecf20Sopenharmony_ci } else 4998c2ecf20Sopenharmony_ci pi_release(dev->pi); 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci } else 5038c2ecf20Sopenharmony_ci for (unit = 0; unit < PG_UNITS; unit++, dev++) { 5048c2ecf20Sopenharmony_ci int *parm = *drives[unit]; 5058c2ecf20Sopenharmony_ci if (!parm[D_PRT]) 5068c2ecf20Sopenharmony_ci continue; 5078c2ecf20Sopenharmony_ci if (pi_init(dev->pi, 0, parm[D_PRT], parm[D_MOD], 5088c2ecf20Sopenharmony_ci parm[D_UNI], parm[D_PRO], parm[D_DLY], 5098c2ecf20Sopenharmony_ci pg_scratch, PI_PG, verbose, dev->name)) { 5108c2ecf20Sopenharmony_ci if (!pg_probe(dev)) { 5118c2ecf20Sopenharmony_ci dev->present = 1; 5128c2ecf20Sopenharmony_ci k++; 5138c2ecf20Sopenharmony_ci } else 5148c2ecf20Sopenharmony_ci pi_release(dev->pi); 5158c2ecf20Sopenharmony_ci } 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci if (k) 5198c2ecf20Sopenharmony_ci return 0; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci pi_unregister_driver(par_drv); 5228c2ecf20Sopenharmony_ci printk("%s: No ATAPI device detected\n", name); 5238c2ecf20Sopenharmony_ci return -1; 5248c2ecf20Sopenharmony_ci} 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_cistatic int pg_open(struct inode *inode, struct file *file) 5278c2ecf20Sopenharmony_ci{ 5288c2ecf20Sopenharmony_ci int unit = iminor(inode) & 0x7f; 5298c2ecf20Sopenharmony_ci struct pg *dev = &devices[unit]; 5308c2ecf20Sopenharmony_ci int ret = 0; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci mutex_lock(&pg_mutex); 5338c2ecf20Sopenharmony_ci if ((unit >= PG_UNITS) || (!dev->present)) { 5348c2ecf20Sopenharmony_ci ret = -ENODEV; 5358c2ecf20Sopenharmony_ci goto out; 5368c2ecf20Sopenharmony_ci } 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci if (test_and_set_bit(0, &dev->access)) { 5398c2ecf20Sopenharmony_ci ret = -EBUSY; 5408c2ecf20Sopenharmony_ci goto out; 5418c2ecf20Sopenharmony_ci } 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci if (dev->busy) { 5448c2ecf20Sopenharmony_ci pg_reset(dev); 5458c2ecf20Sopenharmony_ci dev->busy = 0; 5468c2ecf20Sopenharmony_ci } 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci pg_identify(dev, (verbose > 1)); 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci dev->bufptr = kmalloc(PG_MAX_DATA, GFP_KERNEL); 5518c2ecf20Sopenharmony_ci if (dev->bufptr == NULL) { 5528c2ecf20Sopenharmony_ci clear_bit(0, &dev->access); 5538c2ecf20Sopenharmony_ci printk("%s: buffer allocation failed\n", dev->name); 5548c2ecf20Sopenharmony_ci ret = -ENOMEM; 5558c2ecf20Sopenharmony_ci goto out; 5568c2ecf20Sopenharmony_ci } 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci file->private_data = dev; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ciout: 5618c2ecf20Sopenharmony_ci mutex_unlock(&pg_mutex); 5628c2ecf20Sopenharmony_ci return ret; 5638c2ecf20Sopenharmony_ci} 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_cistatic int pg_release(struct inode *inode, struct file *file) 5668c2ecf20Sopenharmony_ci{ 5678c2ecf20Sopenharmony_ci struct pg *dev = file->private_data; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci kfree(dev->bufptr); 5708c2ecf20Sopenharmony_ci dev->bufptr = NULL; 5718c2ecf20Sopenharmony_ci clear_bit(0, &dev->access); 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci return 0; 5748c2ecf20Sopenharmony_ci} 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_cistatic ssize_t pg_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos) 5778c2ecf20Sopenharmony_ci{ 5788c2ecf20Sopenharmony_ci struct pg *dev = filp->private_data; 5798c2ecf20Sopenharmony_ci struct pg_write_hdr hdr; 5808c2ecf20Sopenharmony_ci int hs = sizeof (hdr); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci if (dev->busy) 5838c2ecf20Sopenharmony_ci return -EBUSY; 5848c2ecf20Sopenharmony_ci if (count < hs) 5858c2ecf20Sopenharmony_ci return -EINVAL; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci if (copy_from_user(&hdr, buf, hs)) 5888c2ecf20Sopenharmony_ci return -EFAULT; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci if (hdr.magic != PG_MAGIC) 5918c2ecf20Sopenharmony_ci return -EINVAL; 5928c2ecf20Sopenharmony_ci if (hdr.dlen < 0 || hdr.dlen > PG_MAX_DATA) 5938c2ecf20Sopenharmony_ci return -EINVAL; 5948c2ecf20Sopenharmony_ci if ((count - hs) > PG_MAX_DATA) 5958c2ecf20Sopenharmony_ci return -EINVAL; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci if (hdr.func == PG_RESET) { 5988c2ecf20Sopenharmony_ci if (count != hs) 5998c2ecf20Sopenharmony_ci return -EINVAL; 6008c2ecf20Sopenharmony_ci if (pg_reset(dev)) 6018c2ecf20Sopenharmony_ci return -EIO; 6028c2ecf20Sopenharmony_ci return count; 6038c2ecf20Sopenharmony_ci } 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci if (hdr.func != PG_COMMAND) 6068c2ecf20Sopenharmony_ci return -EINVAL; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci dev->start = jiffies; 6098c2ecf20Sopenharmony_ci dev->timeout = hdr.timeout * HZ + HZ / 2 + jiffies; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci if (pg_command(dev, hdr.packet, hdr.dlen, jiffies + PG_TMO)) { 6128c2ecf20Sopenharmony_ci if (dev->status & 0x10) 6138c2ecf20Sopenharmony_ci return -ETIME; 6148c2ecf20Sopenharmony_ci return -EIO; 6158c2ecf20Sopenharmony_ci } 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci dev->busy = 1; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci if (copy_from_user(dev->bufptr, buf + hs, count - hs)) 6208c2ecf20Sopenharmony_ci return -EFAULT; 6218c2ecf20Sopenharmony_ci return count; 6228c2ecf20Sopenharmony_ci} 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_cistatic ssize_t pg_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos) 6258c2ecf20Sopenharmony_ci{ 6268c2ecf20Sopenharmony_ci struct pg *dev = filp->private_data; 6278c2ecf20Sopenharmony_ci struct pg_read_hdr hdr; 6288c2ecf20Sopenharmony_ci int hs = sizeof (hdr); 6298c2ecf20Sopenharmony_ci int copy; 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci if (!dev->busy) 6328c2ecf20Sopenharmony_ci return -EINVAL; 6338c2ecf20Sopenharmony_ci if (count < hs) 6348c2ecf20Sopenharmony_ci return -EINVAL; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci dev->busy = 0; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci if (pg_completion(dev, dev->bufptr, dev->timeout)) 6398c2ecf20Sopenharmony_ci if (dev->status & 0x10) 6408c2ecf20Sopenharmony_ci return -ETIME; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci memset(&hdr, 0, sizeof(hdr)); 6438c2ecf20Sopenharmony_ci hdr.magic = PG_MAGIC; 6448c2ecf20Sopenharmony_ci hdr.dlen = dev->dlen; 6458c2ecf20Sopenharmony_ci copy = 0; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci if (hdr.dlen < 0) { 6488c2ecf20Sopenharmony_ci hdr.dlen = -1 * hdr.dlen; 6498c2ecf20Sopenharmony_ci copy = hdr.dlen; 6508c2ecf20Sopenharmony_ci if (copy > (count - hs)) 6518c2ecf20Sopenharmony_ci copy = count - hs; 6528c2ecf20Sopenharmony_ci } 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci hdr.duration = (jiffies - dev->start + HZ / 2) / HZ; 6558c2ecf20Sopenharmony_ci hdr.scsi = dev->status & 0x0f; 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci if (copy_to_user(buf, &hdr, hs)) 6588c2ecf20Sopenharmony_ci return -EFAULT; 6598c2ecf20Sopenharmony_ci if (copy > 0) 6608c2ecf20Sopenharmony_ci if (copy_to_user(buf + hs, dev->bufptr, copy)) 6618c2ecf20Sopenharmony_ci return -EFAULT; 6628c2ecf20Sopenharmony_ci return copy + hs; 6638c2ecf20Sopenharmony_ci} 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_cistatic int __init pg_init(void) 6668c2ecf20Sopenharmony_ci{ 6678c2ecf20Sopenharmony_ci int unit; 6688c2ecf20Sopenharmony_ci int err; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci if (disable){ 6718c2ecf20Sopenharmony_ci err = -EINVAL; 6728c2ecf20Sopenharmony_ci goto out; 6738c2ecf20Sopenharmony_ci } 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci pg_init_units(); 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci if (pg_detect()) { 6788c2ecf20Sopenharmony_ci err = -ENODEV; 6798c2ecf20Sopenharmony_ci goto out; 6808c2ecf20Sopenharmony_ci } 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci err = register_chrdev(major, name, &pg_fops); 6838c2ecf20Sopenharmony_ci if (err < 0) { 6848c2ecf20Sopenharmony_ci printk("pg_init: unable to get major number %d\n", major); 6858c2ecf20Sopenharmony_ci for (unit = 0; unit < PG_UNITS; unit++) { 6868c2ecf20Sopenharmony_ci struct pg *dev = &devices[unit]; 6878c2ecf20Sopenharmony_ci if (dev->present) 6888c2ecf20Sopenharmony_ci pi_release(dev->pi); 6898c2ecf20Sopenharmony_ci } 6908c2ecf20Sopenharmony_ci goto out; 6918c2ecf20Sopenharmony_ci } 6928c2ecf20Sopenharmony_ci major = err; /* In case the user specified `major=0' (dynamic) */ 6938c2ecf20Sopenharmony_ci pg_class = class_create(THIS_MODULE, "pg"); 6948c2ecf20Sopenharmony_ci if (IS_ERR(pg_class)) { 6958c2ecf20Sopenharmony_ci err = PTR_ERR(pg_class); 6968c2ecf20Sopenharmony_ci goto out_chrdev; 6978c2ecf20Sopenharmony_ci } 6988c2ecf20Sopenharmony_ci for (unit = 0; unit < PG_UNITS; unit++) { 6998c2ecf20Sopenharmony_ci struct pg *dev = &devices[unit]; 7008c2ecf20Sopenharmony_ci if (dev->present) 7018c2ecf20Sopenharmony_ci device_create(pg_class, NULL, MKDEV(major, unit), NULL, 7028c2ecf20Sopenharmony_ci "pg%u", unit); 7038c2ecf20Sopenharmony_ci } 7048c2ecf20Sopenharmony_ci err = 0; 7058c2ecf20Sopenharmony_ci goto out; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ciout_chrdev: 7088c2ecf20Sopenharmony_ci unregister_chrdev(major, "pg"); 7098c2ecf20Sopenharmony_ciout: 7108c2ecf20Sopenharmony_ci return err; 7118c2ecf20Sopenharmony_ci} 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_cistatic void __exit pg_exit(void) 7148c2ecf20Sopenharmony_ci{ 7158c2ecf20Sopenharmony_ci int unit; 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci for (unit = 0; unit < PG_UNITS; unit++) { 7188c2ecf20Sopenharmony_ci struct pg *dev = &devices[unit]; 7198c2ecf20Sopenharmony_ci if (dev->present) 7208c2ecf20Sopenharmony_ci device_destroy(pg_class, MKDEV(major, unit)); 7218c2ecf20Sopenharmony_ci } 7228c2ecf20Sopenharmony_ci class_destroy(pg_class); 7238c2ecf20Sopenharmony_ci unregister_chrdev(major, name); 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci for (unit = 0; unit < PG_UNITS; unit++) { 7268c2ecf20Sopenharmony_ci struct pg *dev = &devices[unit]; 7278c2ecf20Sopenharmony_ci if (dev->present) 7288c2ecf20Sopenharmony_ci pi_release(dev->pi); 7298c2ecf20Sopenharmony_ci } 7308c2ecf20Sopenharmony_ci} 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 7338c2ecf20Sopenharmony_cimodule_init(pg_init) 7348c2ecf20Sopenharmony_cimodule_exit(pg_exit) 735