Advertisement
deaphroat

"ps3disk"

Oct 1st, 2012
374
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 16.53 KB | None | 0 0
  1. +#define DEVICE_NAME        "ps3disk"
  2. +
  3. +#define BOUNCE_SIZE        (64*1024)
  4. +
  5. +#define PS3DISK_MAX_DISKS  16
  6. +#define PS3DISK_MINORS     16
  7. +
  8. +#define KERNEL_SECTOR_SIZE 512
  9. +
  10. +
  11. +#define PS3DISK_NAME       "ps3d%c"
  12. +
  13. +
  14. +struct ps3disk_private {
  15. +   spinlock_t lock;        /* Request queue spinlock */
  16. +   struct request_queue *queue;
  17. +   struct gendisk *gendisk;
  18. +   unsigned int blocking_factor;
  19. +   struct request *req;
  20. +   u64 raw_capacity;
  21. +   unsigned char model[ATA_ID_PROD_LEN+1];
  22. +};
  23. +#define ps3disk_priv(dev)  ((dev)->sbd.core.driver_data)
  24. +
  25. +
  26. +#define LV1_STORAGE_SEND_ATA_COMMAND   (2)
  27. +#define LV1_STORAGE_ATA_HDDOUT     (0x23)
  28. +
  29. +struct lv1_ata_cmnd_block {
  30. +   u16 features;
  31. +   u16 sector_count;
  32. +   u16 LBA_low;
  33. +   u16 LBA_mid;
  34. +   u16 LBA_high;
  35. +   u8  device;
  36. +   u8  command;
  37. +   u32 is_ext;
  38. +   u32 proto;
  39. +   u32 in_out;
  40. +   u32 size;
  41. +   u64 buffer;
  42. +   u32 arglen;
  43. +};
  44. +
  45. +enum lv1_ata_proto {
  46. +   NON_DATA_PROTO     = 0,
  47. +   PIO_DATA_IN_PROTO  = 1,
  48. +   PIO_DATA_OUT_PROTO = 2,
  49. +   DMA_PROTO = 3
  50. +};
  51. +
  52. +enum lv1_ata_in_out {
  53. +   DIR_WRITE = 0,          /* memory -> device */
  54. +   DIR_READ = 1            /* device -> memory */
  55. +};
  56. +
  57. +static int ps3disk_major;
  58. +
  59. +
  60. +static struct block_device_operations ps3disk_fops = {
  61. +   .owner      = THIS_MODULE,
  62. +};
  63. +
  64. +
  65. +static void ps3disk_scatter_gather(struct ps3_storage_device *dev,
  66. +                  struct request *req, int gather)
  67. +{
  68. +   unsigned int sectors = 0, offset = 0;
  69. +   struct bio *bio;
  70. +   sector_t sector;
  71. +   struct bio_vec *bvec;
  72. +   unsigned int i = 0, j;
  73. +   size_t size;
  74. +   void *buf;
  75. +
  76. +   rq_for_each_bio(bio, req) {
  77. +       sector = bio->bi_sector;
  78. +       dev_dbg(&dev->sbd.core,
  79. +           "%s:%u: bio %u: %u segs %u sectors from %lu\n",
  80. +           __func__, __LINE__, i, bio_segments(bio),
  81. +           bio_sectors(bio), sector);
  82. +       bio_for_each_segment(bvec, bio, j) {
  83. +           size = bio_cur_sectors(bio)*KERNEL_SECTOR_SIZE;
  84. +           buf = __bio_kmap_atomic(bio, j, KM_IRQ0);
  85. +           if (gather)
  86. +               memcpy(dev->bounce_buf+offset, buf, size);
  87. +           else
  88. +               memcpy(buf, dev->bounce_buf+offset, size);
  89. +           offset += size;
  90. +           flush_kernel_dcache_page(bio_iovec_idx(bio, j)->bv_page);
  91. +           __bio_kunmap_atomic(bio, KM_IRQ0);
  92. +       }
  93. +       sectors += bio_sectors(bio);
  94. +       i++;
  95. +   }
  96. +}
  97. +
  98. +static int ps3disk_submit_request_sg(struct ps3_storage_device *dev,
  99. +                    struct request *req)
  100. +{
  101. +   struct ps3disk_private *priv = ps3disk_priv(dev);
  102. +   int write = rq_data_dir(req), res;
  103. +   const char *op = write ? "write" : "read";
  104. +   u64 start_sector, sectors;
  105. +   unsigned int region_id = dev->regions[dev->region_idx].id;
  106. +
  107. +#ifdef DEBUG
  108. +   unsigned int n = 0;
  109. +   struct bio *bio;
  110. +   rq_for_each_bio(bio, req)
  111. +       n++;
  112. +   dev_dbg(&dev->sbd.core,
  113. +       "%s:%u: %s req has %u bios for %lu sectors %lu hard sectors\n",
  114. +       __func__, __LINE__, op, n, req->nr_sectors,
  115. +       req->hard_nr_sectors);
  116. +#endif
  117. +
  118. +   start_sector = req->sector*priv->blocking_factor;
  119. +   sectors = req->nr_sectors*priv->blocking_factor;
  120. +   dev_dbg(&dev->sbd.core, "%s:%u: %s %lu sectors starting at %lu\n",
  121. +       __func__, __LINE__, op, sectors, start_sector);
  122. +
  123. +   if (write) {
  124. +       ps3disk_scatter_gather(dev, req, 1);
  125. +
  126. +       res = lv1_storage_write(dev->sbd.dev_id, region_id,
  127. +                   start_sector, sectors, 0,
  128. +                   dev->bounce_lpar, &dev->tag);
  129. +   } else {
  130. +       res = lv1_storage_read(dev->sbd.dev_id, region_id,
  131. +                      start_sector, sectors, 0,
  132. +                      dev->bounce_lpar, &dev->tag);
  133. +   }
  134. +   if (res) {
  135. +       dev_err(&dev->sbd.core, "%s:%u: %s failed %d\n", __func__,
  136. +           __LINE__, op, res);
  137. +       end_request(req, 0);
  138. +       return 0;
  139. +   }
  140. +
  141. +   priv->req = req;
  142. +   return 1;
  143. +}
  144. +
  145. +static int ps3disk_submit_flush_request(struct ps3_storage_device *dev,
  146. +                   struct request *req)
  147. +{
  148. +   struct ps3disk_private *priv = ps3disk_priv(dev);
  149. +   u64 res;
  150. +
  151. +   dev_dbg(&dev->sbd.core, "%s:%u: flush request\n", __func__, __LINE__);
  152. +
  153. +   res = lv1_storage_send_device_command(dev->sbd.dev_id,
  154. +                         LV1_STORAGE_ATA_HDDOUT, 0, 0, 0,
  155. +                         0, &dev->tag);
  156. +   if (res) {
  157. +       dev_err(&dev->sbd.core, "%s:%u: sync cache failed 0x%lx\n",
  158. +           __func__, __LINE__, res);
  159. +       end_request(req, 0);
  160. +       return 0;
  161. +   }
  162. +
  163. +   priv->req = req;
  164. +   return 1;
  165. +}
  166. +
  167. +static void ps3disk_do_request(struct ps3_storage_device *dev,
  168. +                  request_queue_t *q)
  169. +{
  170. +   struct request *req;
  171. +
  172. +   dev_dbg(&dev->sbd.core, "%s:%u\n", __func__, __LINE__);
  173. +
  174. +   while ((req = elv_next_request(q))) {
  175. +       if (blk_fs_request(req)) {
  176. +           if (ps3disk_submit_request_sg(dev, req))
  177. +               break;
  178. +       } else if (req->cmd_type == REQ_TYPE_FLUSH) {
  179. +           if (ps3disk_submit_flush_request(dev, req))
  180. +               break;
  181. +       } else {
  182. +           blk_dump_rq_flags(req, DEVICE_NAME " bad request");
  183. +           end_request(req, 0);
  184. +           continue;
  185. +       }
  186. +   }
  187. +}
  188. +
  189. +static void ps3disk_request(request_queue_t *q)
  190. +{
  191. +   struct ps3_storage_device *dev = q->queuedata;
  192. +   struct ps3disk_private *priv = ps3disk_priv(dev);
  193. +
  194. +   if (priv->req) {
  195. +       dev_dbg(&dev->sbd.core, "%s:%u busy\n", __func__, __LINE__);
  196. +       return;
  197. +   }
  198. +
  199. +   ps3disk_do_request(dev, q);
  200. +}
  201. +
  202. +static irqreturn_t ps3disk_interrupt(int irq, void *data)
  203. +{
  204. +   struct ps3_storage_device *dev = data;
  205. +   struct ps3disk_private *priv;
  206. +   struct request *req;
  207. +   int res, read, uptodate;
  208. +   u64 tag, status;
  209. +   unsigned long num_sectors;
  210. +   const char *op;
  211. +
  212. +   res = lv1_storage_get_async_status(dev->sbd.dev_id, &tag, &status);
  213. +
  214. +   if (tag != dev->tag)
  215. +       dev_err(&dev->sbd.core,
  216. +           "%s:%u: tag mismatch, got %lx, expected %lx\n",
  217. +           __func__, __LINE__, tag, dev->tag);
  218. +
  219. +   if (res) {
  220. +       dev_err(&dev->sbd.core, "%s:%u: res=%d status=0x%lx\n",
  221. +           __func__, __LINE__, res, status);
  222. +       return IRQ_HANDLED;
  223. +   }
  224. +
  225. +   priv = ps3disk_priv(dev);
  226. +   req = priv->req;
  227. +   if (!req) {
  228. +       dev_dbg(&dev->sbd.core,
  229. +           "%s:%u non-block layer request completed\n", __func__,
  230. +           __LINE__);
  231. +       dev->lv1_status = status;
  232. +       complete(&dev->done);
  233. +       return IRQ_HANDLED;
  234. +   }
  235. +
  236. +   if (req->cmd_type == REQ_TYPE_FLUSH) {
  237. +       read = 0;
  238. +       num_sectors = req->hard_cur_sectors;
  239. +       op = "flush";
  240. +   } else {
  241. +       read = !rq_data_dir(req);
  242. +       num_sectors = req->nr_sectors;
  243. +       op = read ? "read" : "write";
  244. +   }
  245. +   if (status) {
  246. +       dev_dbg(&dev->sbd.core, "%s:%u: %s failed 0x%lx\n", __func__,
  247. +           __LINE__, op, status);
  248. +       uptodate = 0;
  249. +   } else {
  250. +       dev_dbg(&dev->sbd.core, "%s:%u: %s completed\n", __func__,
  251. +           __LINE__, op);
  252. +       uptodate = 1;
  253. +       if (read)
  254. +           ps3disk_scatter_gather(dev, req, 0);
  255. +   }
  256. +
  257. +   spin_lock(&priv->lock);
  258. +   if (!end_that_request_first(req, uptodate, num_sectors)) {
  259. +       add_disk_randomness(req->rq_disk);
  260. +       blkdev_dequeue_request(req);
  261. +       end_that_request_last(req, uptodate);
  262. +   }
  263. +   priv->req = NULL;
  264. +   ps3disk_do_request(dev, priv->queue);
  265. +   spin_unlock(&priv->lock);
  266. +
  267. +   return IRQ_HANDLED;
  268. +}
  269. +
  270. +static int ps3disk_sync_cache(struct ps3_storage_device *dev)
  271. +{
  272. +   u64 res;
  273. +
  274. +   dev_dbg(&dev->sbd.core, "%s:%u: sync cache\n", __func__, __LINE__);
  275. +
  276. +   res = ps3stor_send_command(dev, LV1_STORAGE_ATA_HDDOUT, 0, 0, 0, 0);
  277. +   if (res) {
  278. +       dev_err(&dev->sbd.core, "%s:%u: sync cache failed 0x%lx\n",
  279. +           __func__, __LINE__, res);
  280. +       return -EIO;
  281. +   }
  282. +   return 0;
  283. +}
  284. +
  285. +
  286. +/* ATA helpers copied from drivers/ata/libata-core.c */
  287. +
  288. +static void swap_buf_le16(u16 *buf, unsigned int buf_words)
  289. +{
  290. +#ifdef __BIG_ENDIAN
  291. +   unsigned int i;
  292. +
  293. +   for (i = 0; i < buf_words; i++)
  294. +       buf[i] = le16_to_cpu(buf[i]);
  295. +#endif /* __BIG_ENDIAN */
  296. +}
  297. +
  298. +static u64 ata_id_n_sectors(const u16 *id)
  299. +{
  300. +   if (ata_id_has_lba(id)) {
  301. +       if (ata_id_has_lba48(id))
  302. +           return ata_id_u64(id, 100);
  303. +       else
  304. +           return ata_id_u32(id, 60);
  305. +   } else {
  306. +       if (ata_id_current_chs_valid(id))
  307. +           return ata_id_u32(id, 57);
  308. +       else
  309. +           return id[1] * id[3] * id[6];
  310. +   }
  311. +}
  312. +
  313. +static void ata_id_string(const u16 *id, unsigned char *s, unsigned int ofs,
  314. +             unsigned int len)
  315. +{
  316. +   unsigned int c;
  317. +
  318. +   while (len > 0) {
  319. +       c = id[ofs] >> 8;
  320. +       *s = c;
  321. +       s++;
  322. +
  323. +       c = id[ofs] & 0xff;
  324. +       *s = c;
  325. +       s++;
  326. +
  327. +       ofs++;
  328. +       len -= 2;
  329. +   }
  330. +}
  331. +
  332. +static void ata_id_c_string(const u16 *id, unsigned char *s, unsigned int ofs,
  333. +               unsigned int len)
  334. +{
  335. +   unsigned char *p;
  336. +
  337. +   WARN_ON(!(len & 1));
  338. +
  339. +   ata_id_string(id, s, ofs, len - 1);
  340. +
  341. +   p = s + strnlen(s, len - 1);
  342. +   while (p > s && p[-1] == ' ')
  343. +       p--;
  344. +   *p = '\0';
  345. +}
  346. +
  347. +static int ps3disk_identify(struct ps3_storage_device *dev)
  348. +{
  349. +   struct ps3disk_private *priv = ps3disk_priv(dev);
  350. +   struct lv1_ata_cmnd_block ata_cmnd;
  351. +   u16 *id = dev->bounce_buf;
  352. +   u64 res;
  353. +
  354. +   dev_dbg(&dev->sbd.core, "%s:%u: identify disk\n", __func__, __LINE__);
  355. +
  356. +   memset(&ata_cmnd, 0, sizeof(struct lv1_ata_cmnd_block));
  357. +   ata_cmnd.command = ATA_CMD_ID_ATA;
  358. +   ata_cmnd.sector_count = 1;
  359. +   ata_cmnd.size = ata_cmnd.arglen = ATA_ID_WORDS * 2;
  360. +   ata_cmnd.buffer = dev->bounce_lpar;
  361. +   ata_cmnd.proto = PIO_DATA_IN_PROTO;
  362. +   ata_cmnd.in_out = DIR_READ;
  363. +
  364. +   res = ps3stor_send_command(dev, LV1_STORAGE_SEND_ATA_COMMAND,
  365. +                  ps3_mm_phys_to_lpar(__pa(&ata_cmnd)),
  366. +                  sizeof(ata_cmnd), ata_cmnd.buffer,
  367. +                  ata_cmnd.arglen);
  368. +   if (res) {
  369. +       dev_err(&dev->sbd.core, "%s:%u: identify disk failed 0x%lx\n",
  370. +           __func__, __LINE__, res);
  371. +       return -EIO;
  372. +   }
  373. +
  374. +   swap_buf_le16(id, ATA_ID_WORDS);
  375. +
  376. +   /* All we're interested in are raw capacity and model name */
  377. +   priv->raw_capacity = ata_id_n_sectors(id);
  378. +   ata_id_c_string(id, priv->model, ATA_ID_PROD, sizeof(priv->model));
  379. +   return 0;
  380. +}
  381. +
  382. +static void ps3disk_prepare_flush(request_queue_t *q, struct request *req)
  383. +{
  384. +   struct ps3_storage_device *dev = q->queuedata;
  385. +
  386. +   dev_dbg(&dev->sbd.core, "%s:%u\n", __func__, __LINE__);
  387. +
  388. +   memset(req->cmd, 0, sizeof(req->cmd));
  389. +   req->cmd_type = REQ_TYPE_FLUSH;
  390. +}
  391. +
  392. +static int ps3disk_issue_flush(request_queue_t *q, struct gendisk *gendisk,
  393. +                  sector_t *sector)
  394. +{
  395. +   struct ps3_storage_device *dev = q->queuedata;
  396. +   struct request *req;
  397. +   int res;
  398. +
  399. +   dev_dbg(&dev->sbd.core, "%s:%u\n", __func__, __LINE__);
  400. +
  401. +   req = blk_get_request(q, WRITE, __GFP_WAIT);
  402. +   ps3disk_prepare_flush(q, req);
  403. +   res = blk_execute_rq(q, gendisk, req, 0);
  404. +   if (res)
  405. +       dev_err(&dev->sbd.core, "%s:%u: flush request failed %d\n",
  406. +           __func__, __LINE__, res);
  407. +   blk_put_request(req);
  408. +   return res;
  409. +}
  410. +
  411. +
  412. +static unsigned long ps3disk_mask;
  413. +
  414. +static int __devinit ps3disk_probe(struct ps3_system_bus_device *_dev)
  415. +{
  416. +   struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);
  417. +   struct ps3disk_private *priv;
  418. +   int error;
  419. +   unsigned int devidx;
  420. +   struct request_queue *queue;
  421. +   struct gendisk *gendisk;
  422. +
  423. +   if (dev->blk_size < KERNEL_SECTOR_SIZE) {
  424. +       dev_err(&dev->sbd.core,
  425. +           "%s:%u: cannot handle block size %lu\n", __func__,
  426. +           __LINE__, dev->blk_size);
  427. +       return -EINVAL;
  428. +   }
  429. +
  430. +   BUILD_BUG_ON(PS3DISK_MAX_DISKS > BITS_PER_LONG);
  431. +   devidx = find_first_zero_bit(&ps3disk_mask, PS3DISK_MAX_DISKS);
  432. +   if (devidx >= PS3DISK_MAX_DISKS) {
  433. +       dev_err(&dev->sbd.core, "%s:%u: Too many disks\n", __func__,
  434. +           __LINE__);
  435. +       return -ENOSPC;
  436. +   }
  437. +   __set_bit(devidx, &ps3disk_mask);
  438. +
  439. +   priv = kzalloc(sizeof(*priv), GFP_KERNEL);
  440. +   if (!priv) {
  441. +       error = -ENOMEM;
  442. +       goto fail;
  443. +   }
  444. +
  445. +   ps3disk_priv(dev) = priv;
  446. +   spin_lock_init(&priv->lock);
  447. +
  448. +   dev->bounce_size = BOUNCE_SIZE;
  449. +   dev->bounce_buf = kmalloc(BOUNCE_SIZE, GFP_DMA);
  450. +   if (!dev->bounce_buf) {
  451. +       error = -ENOMEM;
  452. +       goto fail_free_priv;
  453. +   }
  454. +
  455. +   error = ps3stor_setup(dev, ps3disk_interrupt);
  456. +   if (error)
  457. +       goto fail_free_bounce;
  458. +
  459. +   ps3disk_identify(dev);
  460. +
  461. +   queue = blk_init_queue(ps3disk_request, &priv->lock);
  462. +   if (!queue) {
  463. +       dev_err(&dev->sbd.core, "%s:%u: blk_init_queue failed\n",
  464. +           __func__, __LINE__);
  465. +       error = -ENOMEM;
  466. +       goto fail_teardown;
  467. +   }
  468. +
  469. +   priv->queue = queue;
  470. +   queue->queuedata = dev;
  471. +
  472. +   blk_queue_bounce_limit(queue, BLK_BOUNCE_HIGH);
  473. +
  474. +   blk_queue_max_sectors(queue, dev->bounce_size/KERNEL_SECTOR_SIZE);
  475. +   blk_queue_segment_boundary(queue, -1UL);
  476. +   blk_queue_dma_alignment(queue, dev->blk_size-1);
  477. +   blk_queue_hardsect_size(queue, dev->blk_size);
  478. +
  479. +   blk_queue_issue_flush_fn(queue, ps3disk_issue_flush);
  480. +   blk_queue_ordered(queue, QUEUE_ORDERED_DRAIN_FLUSH,
  481. +             ps3disk_prepare_flush);
  482. +
  483. +   blk_queue_max_phys_segments(queue, -1);
  484. +   blk_queue_max_hw_segments(queue, -1);
  485. +   blk_queue_max_segment_size(queue, dev->bounce_size);
  486. +
  487. +   gendisk = alloc_disk(PS3DISK_MINORS);
  488. +   if (!gendisk) {
  489. +       dev_err(&dev->sbd.core, "%s:%u: alloc_disk failed\n", __func__,
  490. +           __LINE__);
  491. +       error = -ENOMEM;
  492. +       goto fail_cleanup_queue;
  493. +   }
  494. +
  495. +   priv->gendisk = gendisk;
  496. +   gendisk->major = ps3disk_major;
  497. +   gendisk->first_minor = devidx * PS3DISK_MINORS;
  498. +   gendisk->fops = &ps3disk_fops;
  499. +   gendisk->queue = queue;
  500. +   gendisk->private_data = dev;
  501. +   gendisk->driverfs_dev = &dev->sbd.core;
  502. +   snprintf(gendisk->disk_name, sizeof(gendisk->disk_name), PS3DISK_NAME,
  503. +        devidx+'a');
  504. +   priv->blocking_factor = dev->blk_size/KERNEL_SECTOR_SIZE;
  505. +   set_capacity(gendisk,
  506. +            dev->regions[dev->region_idx].size*priv->blocking_factor);
  507. +
  508. +   dev_info(&dev->sbd.core,
  509. +        "%s is a %s (%lu MiB total, %lu MiB for OtherOS)\n",
  510. +        gendisk->disk_name, priv->model, priv->raw_capacity >> 11,
  511. +        get_capacity(gendisk) >> 11);
  512. +
  513. +   add_disk(gendisk);
  514. +   return 0;
  515. +
  516. +fail_cleanup_queue:
  517. +   blk_cleanup_queue(queue);
  518. +fail_teardown:
  519. +   ps3stor_teardown(dev);
  520. +fail_free_bounce:
  521. +   kfree(dev->bounce_buf);
  522. +fail_free_priv:
  523. +   kfree(priv);
  524. +   ps3disk_priv(dev) = NULL;
  525. +fail:
  526. +   __clear_bit(devidx, &ps3disk_mask);
  527. +   return error;
  528. +}
  529. +
  530. +static int ps3disk_remove(struct ps3_system_bus_device *_dev)
  531. +{
  532. +   struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);
  533. +   struct ps3disk_private *priv = ps3disk_priv(dev);
  534. +
  535. +   __clear_bit(priv->gendisk->first_minor / PS3DISK_MINORS,
  536. +           &ps3disk_mask);
  537. +   del_gendisk(priv->gendisk);
  538. +   blk_cleanup_queue(priv->queue);
  539. +   put_disk(priv->gendisk);
  540. +   dev_notice(&dev->sbd.core, "Synchronizing disk cache\n");
  541. +   ps3disk_sync_cache(dev);
  542. +   ps3stor_teardown(dev);
  543. +   kfree(dev->bounce_buf);
  544. +   kfree(priv);
  545. +   ps3disk_priv(dev) = NULL;
  546. +   return 0;
  547. +}
  548. +
  549. +static struct ps3_system_bus_driver ps3disk = {
  550. +   .match_id   = PS3_MATCH_ID_STOR_DISK,
  551. +   .core.name  = DEVICE_NAME,
  552. +   .core.owner = THIS_MODULE,
  553. +   .probe      = ps3disk_probe,
  554. +   .remove     = ps3disk_remove,
  555. +   .shutdown   = ps3disk_remove,
  556. +};
  557. +
  558. +
  559. +static int __init ps3disk_init(void)
  560. +{
  561. +   int error;
  562. +
  563. +   if (!firmware_has_feature(FW_FEATURE_PS3_LV1))
  564. +       return -ENODEV;
  565. +
  566. +   error = register_blkdev(0, DEVICE_NAME);
  567. +   if (error +     printk(KERN_ERR "%s:%u: register_blkdev failed %d\n", __func__,
  568. +              __LINE__, error);
  569. +       return error;
  570. +   }
  571. +   ps3disk_major = error;
  572. +
  573. +   pr_info("%s:%u: registered block device major %d\n", __func__,
  574. +       __LINE__, ps3disk_major);
  575. +
  576. +   error = ps3_system_bus_driver_register(&ps3disk);
  577. +   if (error)
  578. +       unregister_blkdev(ps3disk_major, DEVICE_NAME);
  579. +
  580. +   return error;
  581. +}
  582. +
  583. +static void __exit ps3disk_exit(void)
  584. +{
  585. +   ps3_system_bus_driver_unregister(&ps3disk);
  586. +   unregister_blkdev(ps3disk_major, DEVICE_NAME);
  587. +}
  588. +
  589. +module_init(ps3disk_init);
  590. +module_exit(ps3disk_exit);
  591. +
  592. +MODULE_LICENSE("GPL");
  593. +MODULE_DESCRIPTION("PS3 Disk Storage Driver");
  594. +MODULE_AUTHOR("Sony Corporation");
  595. +MODULE_ALIAS(PS3_MODULE_ALIAS_STOR_DISK);
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement